diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 000000000..6132499e0 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,30 @@ + +environment: + CCACHE_BASEDIR: $CIRRUS_WORKING_DIR + +container: + image: rsmmr/spicy-ci-ubuntu:latest + cpu: 8 + memory: 16G + +clang9_debug_task: + configure_script: ./ci/run-ci -b build-ci-debug configure debug --cxx-compiler clang++-9 --with-zeek /opt/zeek-debug --clang-format /opt/clang10/bin/clang-format --clang-tidy /opt/clang10/bin/clang-tidy + build_script: ./ci/run-ci -b build-ci-debug build + test_build_script: ./ci/run-ci -b build-ci-debug test-build + test_code_script: ./ci/run-ci -b build-ci-debug test-code + install_script: ./ci/run-ci -b build-ci-debug install + + timeout_in: 120m + + ccache_cache: + folder: /var/spool/ccache + fingerprint_script: echo $CIRRUS_TASK_NAME-$CIRRUS_OS + + always: + ci_artifacts: + path: build-ci-debug/ci + + junit_artifacts: + path: build-ci-debug/ci/diag.xml + type: text/xml + format: junit diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..f5e5810e7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,120 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: 'NOLINT' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '^BEGIN_' +MacroBlockEnd: '^END_' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 500 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceAfterLogicalNot: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpacesInConditionalStatement: true +Standard: Cpp11 +StatementMacros: + - STANDARD_OPERATOR_1 +TabWidth: 4 +UseTab: Never +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..fa8db1d0e --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,46 @@ +--- +Checks: 'bugprone, + cert, + clang, + misc, + modernize, + performance, + portability, + readability, + + -bugprone-exception-escape, + -bugprone-forward-declaration-namespace, + -bugprone-macro-parentheses, + -bugprone-suspicious-semicolon, + -bugprone-unhandled-self-assignment, + -cert-err58-cpp, + -cert-oop54-cpp, + -clang-diagnostic-c++2a-designator, + -clang-diagnostic-deprecated-copy, + -clang-diagnostic-range-loop-analysis, + -misc-macro-parentheses, + -misc-non-private-member-variables-in-classes, + -misc-suspicious-semicolon, + -misc-unused-parameters, + -modernize-avoid-c-arrays, + -modernize-use-equals-default, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + -readability-braces-around-statements, + -readability-container-size-empty, + -readability-convert-member-functions-to-static, + -readability-else-after-return, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-make-member-function-const, + -readability-named-parameter, + -readability-qualified-auto, + ' + +HeaderFilterRegex: '/include/(hilti|spicy)/[^3].*/[^_][^_][^/]*$' +WarningsAsErrors: '*' + +CheckOptions: + - key: performance-unnecessary-value-param.AllowedTypes + value: hilti::NodeRef diff --git a/.clang-tidy.ignore b/.clang-tidy.ignore new file mode 100644 index 000000000..b5df6452a --- /dev/null +++ b/.clang-tidy.ignore @@ -0,0 +1,10 @@ +# Paths to source files to ignore when running clang-tidy. This is +# evaluated by scripts/run-clang-tidy. +# +# Note that this doesn't catch header files to ignore. We use a +# combination of HeaderFilterRegex and file names starting with "__" to +# select which headers to skip. + +.*/3rdparty/ +hilti/src/rt/types/regexp.cc # Too many warnings due to usage of jrx C API. +tests/ diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..1b2211df0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +build* diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..df1b4ea19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/build* +tmp +*.pyc +.##* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..d7fa58b33 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/doctest"] + path = 3rdparty/doctest + url = https://github.com/onqtam/doctest.git diff --git a/.paths b/.paths new file mode 100644 index 000000000..896ab0b79 --- /dev/null +++ b/.paths @@ -0,0 +1,17 @@ +hilti/include/ hilti/src/ +hilti/include/ast/ hilti/src/ast/ +hilti/include/base/ hilti/src/base/ +hilti/include/rt/ hilti/src/rt/ +hilti/include/compiler/detail/visitors.h hilti/src/compiler/visitors/ +hilti/include/compiler hilti/src/compiler/ +hilti/include/compiler/detail hilti/src/compiler/ +hilti/bin +spicy/include spicy/src +spicy/include/ast/ spicy/src/ast/ +spicy/include/rt/ spicy/src/rt/ +spicy/include/compiler/detail/visitors.h spicy/src/compiler/visitors/ +spicy/include/compiler spicy/src/compiler/ +spicy/include/compiler/detail spicy/src/compiler/ +spicy/bin +zeek zeek +zeek/plugin/include zeek/plugin/src diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..52851ca80 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: local + hooks: + - id: checkbashisms + name: check for bashisms in /bin/sh scripts + entry: ./tests/Scripts/3rdparty/checkbashisms.pl + language: script + files: '.*\.sh' +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.5.0 + hooks: + - id: trailing-whitespace + exclude: '^tests/Baseline' + - id: end-of-file-fixer + exclude: '^tests/Baseline' + - id: check-yaml + - id: check-added-large-files + +exclude: /3rdparty/|doc/.*examples/|/Baseline/|(\.svg$)|(\.dat$) diff --git a/.update-changes.cfg b/.update-changes.cfg new file mode 100644 index 000000000..89726bdaa --- /dev/null +++ b/.update-changes.cfg @@ -0,0 +1,3 @@ +new_commit_msg="Update CHANGES. [skip ci]" +file_version="" +show_authors=0 diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt new file mode 100644 index 000000000..7b4471707 --- /dev/null +++ b/3rdparty/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(doctest) diff --git a/3rdparty/doctest b/3rdparty/doctest new file mode 160000 index 000000000..932a2ca50 --- /dev/null +++ b/3rdparty/doctest @@ -0,0 +1 @@ +Subproject commit 932a2ca50666138256dae56fbb16db3b1cae133a diff --git a/CHANGES b/CHANGES new file mode 100644 index 000000000..5fcffa71c --- /dev/null +++ b/CHANGES @@ -0,0 +1,4 @@ + +0.4 | 2020-04-07 14:50:17 +0000 + + * Starting CHANGES. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..184d7b63f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,246 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +cmake_minimum_required(VERSION 3.15.0) +project(Spicy ASM C CXX) + +set(flex_minimum_version "2.6") +set(bison_minimum_version "3.4") +set(python_minimum_version "2.4") +set(macos_minimum_version "19.0.0") # macOS 10.15.0 (Catalina) + +## Initialize defaults & global options + +if ( "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" ) + set(LINUX true) +endif () + +if ( NOT CMAKE_BUILD_TYPE ) + # CMake doesn't set build type by default. + set(CMAKE_BUILD_TYPE "Debug") +endif () + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +include(Util) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +include(CheckCompiler) + +include(GNUInstallDirs) +if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY ) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) +endif () + +if( NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY ) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) +endif () + +if ( NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY ) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) +endif () + +make_install_rpath(rpath ${CMAKE_INSTALL_FULL_BINDIR} ${CMAKE_INSTALL_FULL_LIBDIR}) +set(CMAKE_INSTALL_RPATH "${rpath}") + +if ( USE_CCACHE ) + find_program(CCACHE_PROGRAM ccache) + if( CCACHE_PROGRAM ) + set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) + set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) + else () + set(USE_CCACHE "no (error: could not find ccache)") + endif() +else () + set(USE_CCACHE "no") +endif () + +if ( USE_SANITIZERS ) + # Recommended flags per https://github.com/google/sanitizers/wiki/AddressSanitizer + set(sanitizer_cxx_flags "-fsanitize=${USE_SANITIZERS} -fno-omit-frame-pointer -fno-optimize-sibling-calls -O1 -shared-libsan") + set(sanitizer_ld_flags "-fsanitize=${USE_SANITIZERS} -frtlib-add-rpath -shared-libsan") + + if ( LINUX ) + # See hilti/src/asan.cc. + set(sanitizer_ld_flags "${sanitizer_ld_flags} -Wl,-u_sanitizer_options_link_helper") + endif () + + set(HILTI_HAVE_SANITIZER "yes" CACHE BOOL "Using sanitizer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${sanitizer_cxx_flags}") + set(EXTRA_CXX_FLAGS "${EXTRA_CXX_FLAGS} ${sanitizer_cxx_flags}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${sanitizer_ld_flags}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${sanitizer_ld_flags}") + set(EXTRA_LD_FLAGS "${EXTRA_LD_FLAGS} ${sanitizer_ld_flags}") +else () + set(HILTI_HAVE_SANITIZER "no" CACHE BOOL "Using sanitizer") +endif() + +if (USE_WERROR) + set(werror_flags "-Werror") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${werror_flags}") + set(EXTRA_CXX_FLAGS "${EXTRA_CXX_FLAGS} ${werror_flags}") +endif () + +## Load modules + +# If the user specified dedicated prefixes for Flex or Bison, look in these +# prefixes first. As the upstream modules do not support specifying these we +# inject them here by hand. +# +# The implementation relies on the fact that the `find_*` commands do not search +# again should the output variable already be set successfully. We first search +# for the artifacts with `NO_DEFAULT_PATH` and then later trigger the upstream +# `find_package` logic. With that any user-specified prefix takes precedence +# over what could be found in the default search locations. +if ( FLEX_ROOT ) + find_program(FLEX_EXECUTABLE + NAMES flex win_flex + PATHS ${FLEX_ROOT} + PATH_SUFFIXES bin + NO_DEFAULT_PATH) + find_library(FL_LIBRARY + NAMES fl + PATHS ${FLEX_ROOT} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) + find_path(FLEX_INCLUDE_DIR + FlexLexer.h + PATHS ${FLEX_ROOT} + PATH_SUFFIXES include + NO_DEFAULT_PATH) +endif () + +if ( BISON_ROOT) + find_program(BISON_EXECUTABLE + NAMES bison win_bison + PATHS ${BISON_ROOT} + PATH_SUFFIXES bin + NO_DEFAULT_PATH) +endif () + +find_package(Python3) +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) +find_package(ZLIB REQUIRED) +find_package(Backtrace) + +if ( Backtrace_FOUND AND LINUX ) + # On Linux there's a libexecinfo that's not working for us: it seems to break + # when compiling without frame pointers. So we disable that. + if ( "${Backtrace_LIBRARY}" MATCHES "libexecinfo" ) + message(STATUS "Disabling backtrace because we found libexecinfo") + set(Backtrace_FOUND "no") + endif () +endif () + +# Prettify output +if ( Backtrace_FOUND ) + set(HILTI_HAVE_BACKTRACE "yes") +else () + set(HILTI_HAVE_BACKTRACE "no") +endif () + +if ( APPLE ) + set(MACOS_FOUND "yes") + require_version("maccOS" MACOS_FOUND ${CMAKE_SYSTEM_VERSION} "${macos_minimum_version}" true) +endif() + +require_version("Python" Python3_FOUND Python3_VERSION "${python_minimum_version}" true) +require_version("Flex" FLEX_FOUND FLEX_VERSION "${flex_minimum_version}" true) +require_version("Bison" BISON_FOUND BISON_VERSION "${bison_minimum_version}" true) + +find_package(GoldLinker) +find_package(Filesystem REQUIRED COMPONENTS Experimental Final) +find_package(ClangJIT) +find_package(Zeek) + +if ( NOT CLANG_JIT_FOUND ) + set(HILTI_HAVE_JIT no) +endif () + +if ( ZEEK_FOUND ) + set(HAVE_ZEEK yes) + add_subdirectory(zeek) +else () + set(HAVE_ZEEK no) +endif () + +# Set up testing infrastructure. +enable_testing() + +## Process subdirectories +add_subdirectory(hilti) +add_subdirectory(spicy) +add_subdirectory(3rdparty) + +# Global test target +add_custom_target(check COMMAND ctest --output-on-failure -C $ DEPENDS tests) +add_custom_target(tests DEPENDS hilti-rt-tests hilti-tests spicy-tests) + +## Print build summary +string(TOUPPER ${CMAKE_BUILD_TYPE} BuildType) + +string(STRIP "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BuildType}}" cflags) +string(STRIP "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BuildType}}" cxxflags) + +get_target_property(cxx_flags_hilti_rt hilti-rt COMPILE_FLAGS) + +if ( NOT CLANG_RESOURCE_DIR ) + # Just for display. + set(CLANG_RESOURCE_DIR "n/a") +endif () + +execute_process(COMMAND ${PROJECT_SOURCE_DIR}/scripts/autogen-version + OUTPUT_VARIABLE VERSION) +string(STRIP "${VERSION}" VERSION) + +message( + "\n====================| Spicy Build Summary |====================" + "\n" + "\nVersion: ${VERSION}" + "\n" + "\nBuild type: ${CMAKE_BUILD_TYPE}" + "\nBuild directory: ${CMAKE_BINARY_DIR}" + "\nInstall prefix: ${CMAKE_INSTALL_PREFIX}" + "\nBuild shared libs: ${BUILD_SHARED_LIBS}" + "\n" + "\nHost system: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION} (${CMAKE_SYSTEM_PROCESSOR})" + "\nC compiler: ${CMAKE_C_COMPILER} (${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION})" + "\nC++ compiler: ${CMAKE_CXX_COMPILER} (${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION})" + "\n" + "\nJIT enabled: ${HILTI_HAVE_JIT}" + "\nZeek plugin enabled: ${HAVE_ZEEK}" + "\n" + "\nUse ccache: ${USE_CCACHE}" + "\nUse gold linker: ${USE_GOLD}" + "\nUse sanitizers: ${HILTI_HAVE_SANITIZER}" + "\nUse backtrace: ${HILTI_HAVE_BACKTRACE}" + "\n" + "\nClang/LLVM found: ${CLANG_JIT_FOUND}" + "\nClang version: ${CLANG_VERSION}" + "\nLLVM version: ${LLVM_VERSION}" + "\nLLVM prefix: ${LLVM_ROOT}" + "\nClang prefix: ${CLANG_ROOT}" + "\nClang executable: ${CLANG_EXECUTABLE}" + "\nClang resource dir: ${CLANG_RESOURCE_DIR}" + "\nC sys include dirs: ${C_SYSTEM_INCLUDE_DIRS}" + "\nC++ sys include dirs: ${CXX_SYSTEM_INCLUDE_DIRS}" + "\nClang gcc tolchain: ${CLANG_GCC_INSTALLATION}" + "\nWarnings are errors: ${USE_WERROR}" + "\n" + "\nZeek found: ${ZEEK_FOUND}" + "\nZeek version: ${ZEEK_VERSION}" + "\nZeek debug build: ${ZEEK_DEBUG_BUILD}" + "\nZeek root dir: ${ZEEK_PREFIX}" + "\n" + "\nBison version: ${BISON_VERSION}" + "\nCMake version: ${CMAKE_VERSION}" + "\nFlex version: ${FLEX_VERSION}" + "\nPython version: ${Python3_VERSION}" + "\nzlib version: ${ZLIB_VERSION_STRING}" + "\n" + "\n================================================================\n" +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..fef02f85b --- /dev/null +++ b/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2020 by the Zeek Project through the International +Computer Science Institute. 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 name of the Zeek Project, the International Computer + Science Institute, nor the names of 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. + +Note that some files in the distribution may carry their own copyright +notices, see LICENSE.3rdparty. diff --git a/LICENSE.3rdparty b/LICENSE.3rdparty new file mode 100644 index 000000000..556329966 --- /dev/null +++ b/LICENSE.3rdparty @@ -0,0 +1,608 @@ +================================================================================ +Spicy subcomponents: + +The Spicy project contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + + +================================================================================ +For SafeInt (hilti/include/3rdparty/SafeInt): +================================================================================ + +MIT License + +Copyright (c) 2018 Microsoft + +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. + + +================================================================================ +For doctest (3rdparty/doctest): +================================================================================ + +The MIT License (MIT) + +Copyright (c) 2016-2019 Viktor Kirilov + +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. + + +================================================================================ +For EnumClass (hilti/include/3rdparty/enum-class): +================================================================================ + +BSD 2-Clause License + +Copyright (c) 2017, Gabriel Aubut-Lussier +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. + +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 HOLDER 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. + + +================================================================================ +For libtask (hilti/include/3rdparty/libtask): +================================================================================ + +This software was developed as part of a project at MIT. + +Copyright (c) 2005-2007 Russ Cox, + Massachusetts Institute of Technology + +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. + +=== + +Contains parts of an earlier library that has: + +/* + * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox + * Copyright (c) 2003 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + + +================================================================================ +For nlohmann-json (hilti/include/3rdparty/nlohmann-json): +================================================================================ + +MIT License + +Copyright (c) 2013-2018 Niels Lohmann + +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. + + +================================================================================ +For tinyformat (hilti/include/3rdparty/tinyformat): +================================================================================ + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +================================================================================ +For justrx (hilti/src/3rdparty/justrx): +================================================================================ + +Copyright (c) 2014-2016, International Computer Science Institute. 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 name of the International Computer Science Institute, + nor the names of 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. + + +================================================================================ +For libtask (hilti/src/3rdparty/libtask): +================================================================================ + +This software was developed as part of a project at MIT. + +Copyright (c) 2005-2007 Russ Cox, + Massachusetts Institute of Technology + +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. + +=== + +Contains parts of an earlier library that has: + +/* + * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox + * Copyright (c) 2003 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + + +================================================================================ +For pathfind (hilti/src/3rdparty/pathfind): +================================================================================ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + +================================================================================ +For utf8proc (hilti/src/3rdparty/utf8proc): +================================================================================ + +## utf8proc license ## + +**utf8proc** is a software package originally developed +by Jan Behrens and the rest of the Public Software Group, who +deserve nearly all of the credit for this library, that is now maintained by the Julia-language developers. Like the original utf8proc, +whose copyright and license statements are reproduced below, all new +work on the utf8proc library is licensed under the [MIT "expat" +license](http://opensource.org/licenses/MIT): + +*Copyright © 2014-2019 by Steven G. Johnson, Jiahao Chen, Tony Kelman, Jonas Fonseca, and other contributors listed in the git history.* + +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. + +## Original utf8proc license ## + +*Copyright (c) 2009, 2013 Public Software Group e. V., Berlin, Germany* + +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. + +## Unicode data license ## + +This software distribution contains derived data from a modified version of +the Unicode data files. The following license applies to that data: + +**COPYRIGHT AND PERMISSION NOTICE** + +*Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html.* + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Unicode data files and any associated documentation (the "Data +Files") or Unicode software and any associated documentation (the +"Software") to deal in the Data Files or Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, and/or sell copies of the Data Files or Software, and +to permit persons to whom the Data Files or Software are furnished to do +so, provided that (a) the above copyright notice(s) and this permission +notice appear with all copies of the Data Files or Software, (b) both the +above copyright notice(s) and this permission notice appear in associated +documentation, and (c) there is clear notice in each modified Data File or +in the Software as well as in the documentation associated with the Data +File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE 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 OF +THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR +CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be +registered in some jurisdictions. All other trademarks and registered +trademarks mentioned herein are the property of their respective owners. + + +================================================================================ +For run-clang-format scripts/3rdparty/run-clang-format): +================================================================================ + +MIT License + +Copyright (c) 2017 Guillaume Papin + +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. + + +================================================================================ +For libb64 (spicy/src/3rdparty/libb64): +================================================================================ + +Copyright-Only Dedication (based on United States law) +or Public Domain Certification + +The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain of the +country from which the work is published, or (b) hereby dedicates whatever +copyright the dedicators holds in the work of authorship identified below (the +"Work") to the public domain. A certifier, moreover, dedicates any copyright +interest he may have in the associated work, and for these purposes, is +described as a "dedicator" below. + +A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him from +liability if in fact the work certified is not in the public domain. + +Dedicator makes this dedication for the benefit of the public at large and to +the detriment of the Dedicator's heirs and successors. Dedicator intends this +dedication to be an overt act of relinquishment in perpetuity of all present +and future rights under copyright law, whether vested or contingent, in the +Work. Dedicator understands that such relinquishment of all rights includes +the relinquishment of all rights to enforce (by lawsuit or otherwise) those +copyrights in the Work. + +Dedicator recognizes that, once placed in the public domain, the Work may be +freely reproduced, distributed, transmitted, used, modified, built upon, or +otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..7209b1531 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ + +all: build + +.PHONY: build doc + +build: + @if [ -e build/Makefile ]; then $(MAKE) -C build; else true; fi + @if [ -e build/build.ninja ]; then ninja -C build; else true; fi + +install: + @if [ -e build/Makefile ]; then $(MAKE) -C build install; else true; fi + @if [ -e build/build.ninja ]; then ninja -C build install; else true; fi + +doc: + $(MAKE) -C doc + +test: + @cat build/CMakeCache.txt | grep -q HAVE_JIT.*yes && cd tests && btest -j -f diag.log + @cat build/CMakeCache.txt | grep -q HAVE_JIT.*no && cd tests && btest -j -g no-jit -f diag.log + +test-core: + @cd tests && btest -j -g spicy-core -f diag.log + +clean: + @if [ -e build/Makefile ]; then $(MAKE) -C build clean; else true; fi + @if [ -e build/build.ninja ]; then ninja -C build clean; else true; fi + +real-clean: + rm -rf build + +format: + ./scripts/run-clang-format + +format-fixit: + ./scripts/run-clang-format --fixit + +tidy: + ./scripts/run-clang-tidy -j 10 build + +tidy-fixit: + ./scripts/run-clang-tidy -j 10 --fixit build + ./scripts/run-clang-format --fixit + +check: test format tidy diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..e3a6e9cce --- /dev/null +++ b/README.rst @@ -0,0 +1,90 @@ +Spicy — A C++ Parser Generator for Dissecting Protocols & Files +=============================================================== + +Overview +-------- + +Spicy is a C++ parser generator that makes it easy to create robust +parsers for network protocols, file formats, and more. Spicy is a bit +like a "yacc for protocols", but it's much more than that: It's an +all-in-one system enabling developers to write attributed grammars +that define both syntax and semantics of an input format using a +single, unified language. Think of Spicy as a domain-specific +scripting language for all your parsing needs. + +The Spicy toolchain turns such grammars into efficient C++ parsing +code that exposes an API to host applications for instantiating +parsers, feeding them input, and retrieving their results. At runtime, +parsing proceeds fully incrementally—and potentially highly +concurrently—on input streams of arbitrary size. Compilation of Spicy +parsers takes place either just-in-time at startup (through +Clang/LLVM), or ahead-of-time either by creating pre-compiled shared +libraries or simply by giving you C++ code that you can link into your +application. + +Spicy comes with a `Zeek `_ plugin that enables +adding new protocols to Zeek without having to write any C++ code. You +define the grammar, specify which Zeek events to generate, and Spicy +takes care of the rest. + +Download +-------- + +There's are no releases yet. To download the source code from GitHub, +run:: + + # git clone --recursive https://github.com/zeek/spicy + +Getting Started +--------------- + +Read the documentation: + + - `Spicy Manual `_ + * `Installation `_ + * `Getting Started `_ + * `FAQ `_ + +Getting in Touch +---------------- + +Having trouble using Spicy? Have ideas how to make Spicy better? We'd +like to hear from you! + + - Check out the `FAQ `_ to see if any of that helps. + - Report issues on `GitHub `_. + - Ask the ``#spicy`` channel on `Zeek's Slack `_ + - Send mail to the `Spicy mailing list `_. + * We also have a separate read-only `mailing list for following git + activity `_ + +Status +------ + +Spicy is currently in a very early beta phase, it's *not* yet ready +for production usage. You'll find plenty rough edges still, including +unstable code, missing features, and confusing error messages if you +do something unexpected. Specifics of the language and the toolchain +may still change as well---there's no release yet, just a git +``master`` branch that keeps moving. We don't recommend Spicy and its +parsers for anything critical yet, but we're very interested in +feedback as we're working to stabilize all this. + +License +------- + +Spicy is open source and released under a BSD license, which allows +for pretty much unrestricted use as long as you leave the license +header in place. You fully own any parsers that Spicy generates from +your grammars. + +History +------- + +Spicy was originally developed as a research prototype at the +`International Computer Science Institute +`_ with funding from the `U.S. National +Science Foundation `_. Since then, Spicy has been +rebuilt from the ground up by `Corelight +`_, who has contributed the new +implementation to the Zeek Project. diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 000000000..5f7b135b4 --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,93 @@ +FROM ubuntu:eoan + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +CMD sh + +ENV DEBIAN_FRONTEND=noninteractive +ENV CCACHE_DIR=/var/spool/ccache +ENV CCACHE_HASHDIR=1 +ENV CCACHE_MAXSIZE=1G + +RUN apt-get update +RUN apt-get install -y docker.io curl + +# Some helpers +RUN apt-get install -y vim + +### Zeek + + # Dependencies +RUN apt-get install -y make gcc g++ flex bison libpcap-dev libssl-dev python-dev swig zlib1g-dev ninja-build +RUN apt-get install -y git + +WORKDIR /usr/local/cmake +RUN curl -L https://github.com/Kitware/CMake/releases/download/v3.15.0/cmake-3.15.0-Linux-x86_64.tar.gz | tar xzvf - -C /usr/local/cmake --strip-components 1 +ENV PATH="/usr/local/cmake/bin:${PATH}" + + # Release version - 3.0 +RUN mkdir -p /opt/zeek-3.0-release/src && \ + cd /opt/zeek-3.0-release/src && \ + git clone --branch release/3.0 --recursive https://github.com/zeek/zeek.git && \ + cd zeek && \ + ./configure --prefix=/opt/zeek-3.0-release --generator=Ninja && \ + cd build && \ + ninja install && \ + cd .. && \ + rm -rf build + + # Debug version - 3.0 +RUN mkdir -p /opt/zeek-3.0-debug/src && \ + cd /opt/zeek-3.0-debug/src && \ + git clone --branch release/3.0 --recursive https://github.com/zeek/zeek.git && \ + cd zeek && \ + ./configure --prefix=/opt/zeek-3.0-debug --generator=Ninja --enable-debug && \ + cd build && \ + ninja install && \ + cd .. && \ + rm -rf build + + # Release version - 3.1 +RUN mkdir -p /opt/zeek-3.1-release/src && \ + cd /opt/zeek-3.1-release/src && \ + git clone --branch release/3.1 --recursive https://github.com/zeek/zeek.git && \ + cd zeek && \ + ./configure --prefix=/opt/zeek-3.1-release --generator=Ninja && \ + cd build && \ + ninja install && \ + cd .. && \ + rm -rf build + + # Debug version - 3.1 +RUN mkdir -p /opt/zeek-3.1-debug/src && \ + cd /opt/zeek-3.1-debug/src && \ + git clone --branch release/3.1 --recursive https://github.com/zeek/zeek.git && \ + cd zeek && \ + ./configure --prefix=/opt/zeek-3.1-debug --generator=Ninja --enable-debug && \ + cd build && \ + ninja install && \ + cd .. && \ + rm -rf build + +### Spicy + +RUN apt-get -y update + + # Code +RUN apt-get install -y ninja-build ccache bison flex python3 python3-pip docker zlib1g-dev jq locales-all clang-9 libclang-9-dev +RUN pip3 install btest pre-commit + + # Still need to build clang 10 ourselves. +RUN mkdir -p /opt/clang10/src && \ + cd /opt/clang10/src && \ + git clone --branch release/10.x --single-branch --recursive https://github.com/llvm/llvm-project.git && \ + cd llvm-project && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_INSTALL_PREFIX=/opt/clang10 -DLLVM_ENABLE_PROJECTS="clang;compiler-rt;clang-tools-extra" -DLLVM_TARGETS_TO_BUILD=host -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON -G Ninja ../llvm && \ + ninja install && \ + cd ../.. && \ + rm -rf /opt/clang10/src + + # Docs +RUN apt-get install -y python3-sphinx python3-sphinx-rtd-theme awscli diff --git a/ci/Makefile b/ci/Makefile new file mode 100644 index 000000000..3d7220d2d --- /dev/null +++ b/ci/Makefile @@ -0,0 +1,17 @@ + +IMAGE=rsmmr/spicy-ci-ubuntu + +all: + +docker-build: + (cd .. && docker build -t $(IMAGE) -f ci/Dockerfile .) + +docker-build-no-cache: + (cd .. && docker build -t $(IMAGE) -f ci/Dockerfile .) + +docker-run: + docker run --cap-add SYS_PTRACE -i -t $(IMAGE) /bin/bash + +docker-push: + docker tag ${IMAGE} $(IMAGE):latest + docker push $(IMAGE):latest diff --git a/ci/run-ci b/ci/run-ci new file mode 100755 index 000000000..6a9ca0443 --- /dev/null +++ b/ci/run-ci @@ -0,0 +1,394 @@ +#! /usr/bin/env bash +# +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +# +# Runs selected stages of the CI pipeline. Each stage assumes the +# preceesing one has been successfully executed already. +# +# We use bash here since that's useful (for pipefail in particular) and +# users are unlikely to run this script. + +set -o pipefail + +name=$(basename $0) +root=$(cd $(dirname $0)/.. && pwd) +build= +install= + +color_red=$'\e[1;31m' +color_green=$'\e[1;32m' +color_yellow=$'\e[1;33m' +color_blue=$'\e[1;34m' +color_magenta=$'\e[1;35m' +color_cyan=$'\e[1;36m' +color_normal=$'\e[0m' + +function usage { +cat <] [] + +Stages + + configure [release|debug] [] Configure the build for compiling a release/debug version + build Compile the code + install Install the built version + test-code Run code & formatting checks + test-build Run the test suite on the built version + test-install Run the test suite on the installed version + test-install-nojit Run the test suite on the installed version, limit to non-JIT tests only + cleanup Delete all build artifcats. + +Global options: + + -r Base directory of repository checkout (default: ${root}) + -b Build directory; will be deleted at completion (default: \${root}/build-ci) + +Configure options: + + --clang-format Path to clang-format to use (default: found in PATH) + --clang-tidy Path to clang-tidy to use (default: found in PATH) + --cxx-compiler Path to C++ compiler to use (default: found by cmake) + --disable-jit Do not compile in JIT support + --rpath Additional rpaths to use; will end up in LD_LIBRARY_PATH (default: none) + --with-zeek Path to Zeek installation to use (default: none) + --zeek-ld-preload LD_PRELOAD library for Zeek tests (no default; needed for ASAN builds to load runtime) + +EOF + +exit 1 +} + +function log_colored { + color=$1 + shift + printf "%s" "${color}" + printf "%s" "$@" + printf "%s\n" "${color_normal}" +} + +function log_stage { + echo + log_colored "${color_magenta}" "### $@" +} + +function log_warning { + log_colored "${color_yellow}" "### $@" +} + +function log_error { + log_colored "${color_red}" "### $@" +} + +function log_diag { + log_colored "${color_yellow}" "--- $@" +} + +function error { + echo "### Error: $@" + exit 1 +} + +function run_configure { + build_type="$1" + shift + + mkdir -p ${install} + configure="./configure --builddir=${build} --prefix=${install} --generator=Ninja --enable-werror --enable-ccache" + + clang_format=$(which clang-format 2>/dev/null) + clang_tidy=$(which clang-tidy 2>/dev/null) + zeek_root="" + rpath="" + zeek_ld_preload="" + + if [ "${build_type}" == "release" ]; then + : + elif [ "${build_type}" == "debug" ]; then + configure="${configure} --enable-debug --enable-sanitizer" + else + usage + fi + + while [ $# -ne 0 ]; do + case "$1" in + --cxx-compiler) + test $# -gt 0 || usage + configure="${configure} --with-cxx-compiler=$2" + shift 2; + ;; + + --clang-format) + test $# -gt 0 || usage + clang_format="$2" + test -x ${clang_format} || error "clang-format not found in $2" + shift 2; + ;; + + --clang-tidy) + test $# -gt 0 || usage + clang_tidy="$2" + test -x ${clang_tidy} || error "clang-tidy not found in $2" + shift 2; + ;; + + --with-zeek) + test $# -gt 0 || usage + zeek_root="$2" + test -x ${zeek_root}/bin/zeek || error "Zeek not found in $2/bin" + configure="${configure} --with-zeek=${zeek_root}" + shift 2; + ;; + + --rpath) + test $# -gt 0 || usage + rpath="$2" + shift 2; + ;; + + --zeek-ld-preload) + test $# -gt 0 || usage + zeek_ld_preload="$2" + test -e ${zeek_ld_preload} || error "zeek_ld_preload path not found: $2" + shift 2; + ;; + + --disable-jit) + configure="${configure} --disable-jit" + shift 1; + ;; + + --enable-werror) + configure="${configure} --enable-werror" + shift 1; + ;; + + *) usage;; + esac + done + + if [ -e ${build} ]; then + error "Build directory ${build} already exists, delete first" + fi + + test -z "${clang_format}" && log_stage "Warning: No clang-format found, will skip any related tests" + test -z "${clang_tidy}" && log_stage "Warning: No clang-tidy found, will skip any related tests" + + # Looks like Cirrus CI doesn't fetch tags. + git fetch --tags + + log_stage "Running configure ... (${configure})" + ${configure} || exit 1 + mkdir -p ${artifacts} + + echo "${clang_format}" >${build}/.clang_format + echo "${clang_tidy}" >${build}/.clang_tidy + echo "${rpath}" >${build}/.rpath + echo "${zeek_ld_preload}" >${build}/.zeek_ld_preload +} + +function run_build { + export LD_LIBRARY_PATH=$(cat ${build}/.rpath) + + log_stage "Building code ..." + (cd ${build} && ninja) || exit 1 + + log_stage "Building docs ..." + (cd ${root}/doc && make BUILDDIR=${build} DESTDIR=${artifacts}/doc) || exit 1 +} + +function run_install { + log_stage "Installing code ..." + (cd ${build} && ninja install) || exit 1 +} + +function run_test_btest { + if [ -n "$1" ]; then + btest_alternative="-a $1" + else + btest_alternative="" + fi + + if [ -n "${SPICY_BTEST_GROUPS}" ]; then + btest_group="-g no-jit" + else + btest_group="" + fi + + export LD_LIBRARY_PATH=$(cat ${build}/.rpath) + export ZEEK_LD_PRELOAD=$(cat ${build}/.zeek_ld_preload) + + log_stage "Running tests ... (${btest_alternative} ${btest_group})" + + cd tests + eval ${preload} \ + btest -b \ + -j 5 \ + -f ${artifacts}/diag.log \ + -x ${artifacts}/diag.xml \ + ${btest_alternative} \ + ${btest_group} + + rc=$? + + if [ ${rc} != 0 ]; then + cp -a .tmp ${artifacts}/btest-tmp + log_diag "Begin diagnostics" + cat ${artifacts}/diag.log + log_diag "End diagnostics (complete test output in 'btest-tmp')" + log_error "Tests have failed" + fi + + return ${rc} +} + +function run_test_clang_format { + clang_format=$(cat ${build}/.clang_format 2>/dev/null) + + if [ ! -x ${clang_format} ]; then + log_warning "clang-format not available, skipping" + return 0 + fi + + log_stage "Running clang-format ..." + + export CLANG_FORMAT=${clang_format} + + if ${root}/scripts/run-clang-format; then + echo "clang-format run passed" + return 0 + else + ${root}/scripts/run-clang-format >${artifacts}/clang-format.diff + log_diag "Patch to fix in 'clang-format.diff'" + log_error "clang-format has failed" + return 1 + fi +} + +function run_test_clang_tidy { + clang_tidy=$(cat ${build}/.clang_tidy 2>/dev/null) + + if [ ! -x ${clang_tidy} ]; then + log_warning "clang-tidy not available, skipping" + return 0 + fi + + log_stage "Running clang-tidy ..." + + export CLANG_TIDY=${clang_tidy} + + if ${root}//scripts/run-clang-tidy --fixit -j 10 ${build}; then + echo "clang-tidy run passed" + return 0 + else + git diff >${artifacts}/clang-tidy.diff + log_diag "Patch to what can be fixed automatically in 'clang-tidy.diff'" + log_error "clang-tidy has failed" + return 1 + fi +} + +function run_test_code { + rc=0 + + pre-commit run -a || rc=1 + + pushd $(pwd) >/dev/null + run_test_clang_format || rc=1 + popd >/dev/null + + pushd $(pwd) >/dev/null + run_test_clang_tidy || rc=1 + popd >/dev/null + + return ${rc} +} + +function run_test_common { + btest_alternative=$1 + rc=0 + + pushd $(pwd) >/dev/null + run_test_btest ${btest_alternative} || rc=1 + popd >/dev/null + + return ${rc} +} + +function run_test_build { + export SPICY_BUILD_DIRECTORY=${build} + run_test_common +} + +function run_test_install { + export SPICY_INSTALLATION_DIRECTORY=${install} + run_test_common installation +} + +function run_test_install_nojit { + export SPICY_INSTALLATION_DIRECTORY=${install} + export SPICY_BTEST_GROUPS=no-jit + run_test_common installation +} + +function run_deploy { + # log_stage "Deploying docs ..." + log_stage "Deploying docs NOT implemeneted ..." + # TODO: RTD integration +} + +function run_cleanup { + log_stage "Cleaning up ..." + (cd ${build} && ninja clean) || exit 1 + (cd doc && make clean) || exit 1 +} + +### Main + +while getopts "r:b:" opt; do + case "${opt}" in + h) + usage + ;; + r) + root=${OPTARG} + ;; + b) + build=${OPTARG} + ;; + *) + echo "unknown option -${opt}" + exit 1 + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${build}" ]; then + build=${root}/build-ci +fi + +install=/opt/ci-install-$(basename ${build}) +root=$(realpath ${root}) +build=$(realpath ${build}) +artifacts=${build}/ci + +cmd=$1 +shift + +test -n "${cmd}" || usage + +cd ${root} +test -e CMakeLists.txt || error "${root} is not the project's root git repository" + +case "${cmd}" in + configure) (run_configure $@);; + build) (run_build $@);; + install) (run_install $@);; + test-build) (run_test_build $@);; + test-install) (run_test_install $@);; + test-install-nojit) (run_test_install_nojit $@);; + test-code) (run_test_code $@);; + deploy) (run_deploy $@);; + cleanup) (run_cleanup $@);; + *) usage;; +esac diff --git a/cmake/ASTOperators.cmake b/cmake/ASTOperators.cmake new file mode 100644 index 000000000..020942f41 --- /dev/null +++ b/cmake/ASTOperators.cmake @@ -0,0 +1,29 @@ + +# TODO: Clean this up. Turn into functions with named parameters. + +### Autogenerate *.decl file for a set of operator definitions. +macro(autogen_operators outputs ns srcdir dst_decls dst_impls) + set(_output_decls "${dst_decls}") + set(_output_impls "${dst_impls}") + + file(GLOB _headers ${CMAKE_CURRENT_SOURCE_DIR}/${srcdir}/*.h) + + add_custom_command( + COMMAND ${CMAKE_SOURCE_DIR}/scripts/autogen-operators-nodes-decl ${ns} ${CMAKE_CURRENT_SOURCE_DIR}/${srcdir} >${_output_decls} + DEPENDS ${CMAKE_SOURCE_DIR}/scripts/autogen-operators-nodes-decl ${_headers} + OUTPUT ${_output_decls} + ) + + set_source_files_properties(${_output_decls} PROPERTIES GENERATED TRUE) + + ## + + add_custom_command( + COMMAND ${CMAKE_SOURCE_DIR}/scripts/autogen-operators-implementations ${ns} ${CMAKE_CURRENT_SOURCE_DIR}/${srcdir} >${_output_impls} + DEPENDS ${CMAKE_SOURCE_DIR}/scripts/autogen-operators-implementations ${_headers} + OUTPUT "${AUTOGEN_CC}/operators-implementations.cc" + ) + + set_source_files_properties(${_output_impls} PROPERTIES GENERATED TRUE) + list(APPEND ${outputs} ${_output_impls}) +endmacro() diff --git a/cmake/CheckCompiler.cmake b/cmake/CheckCompiler.cmake new file mode 100644 index 000000000..dc1c9ffe5 --- /dev/null +++ b/cmake/CheckCompiler.cmake @@ -0,0 +1,57 @@ +# Adapted from Zeek. + +# 9.0 works +# 10.x is untested +set(clang_minimum_version "9.0") + +# 11.0 comes with 10.15 (Catalina) and works +set(apple_clang_minimum_version "11.0") + +# 7.x is not compiling HILTI/Spicy, although the standard library seems to be recent enough. +# 8.x is untested. +# 9.1.1 works. +set(gcc_minimum_version "9.0") + +include(CheckCXXSourceCompiles) + +macro(cxx17_compile_test) + check_cxx_source_compiles(" + #include + int main() { std::optional a; }" + cxx17_works) + + if (NOT cxx17_works) + message(FATAL_ERROR "failed using C++17 for compilation") + endif () +endmacro() + +if ( CMAKE_CXX_COMPILER_ID STREQUAL "GNU" ) + if ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_minimum_version} ) + message(FATAL_ERROR "GCC version must be at least " + "${gcc_minimum_version} for C++17 support, detected: " + "${CMAKE_CXX_COMPILER_VERSION}") + endif () + +elseif ( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) + if ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${clang_minimum_version} ) + message(FATAL_ERROR "Clang version must be at least " + "${clang_minimum_version} for C++17 support, detected: " + "${CMAKE_CXX_COMPILER_VERSION}") + endif () + if ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5 ) + set(cxx17_flag "-std=c++1z") + endif () +elseif ( CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" ) + if ( CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${apple_clang_minimum_version} ) + message(FATAL_ERROR "Apple Clang version must be at least " + "${apple_clang_minimum_version} for C++17 support, detected: " + "${CMAKE_CXX_COMPILER_VERSION}") + endif () +else() + # Unrecognized compiler: fine to be permissive of other compilers as long + # as they are able to support C++17 and can compile the test program, but + # we just won't be able to give specific advice on what compiler version a + # user needs in the case it actually doesn't support C++17. +endif () + +cxx17_compile_test() diff --git a/cmake/FindClangJIT.cmake b/cmake/FindClangJIT.cmake new file mode 100644 index 000000000..a87f817d9 --- /dev/null +++ b/cmake/FindClangJIT.cmake @@ -0,0 +1,177 @@ +## LLVM/Clang for JIT. Also see see http://llvm.org/docs/CMake.html#embedding-llvm-in-your-project. +## +## The module considers the following variables: +## +## CLANG_ROOT Path to prefix of clang installation +## CMAKE_CXX_COMPILER If that's a clang, we use it to find the pieces we need. +## LLVM_ROOT Path to prefix of LLVM installation +## +## The module sets the following variables +## +## CLANG_JIT_FOUND True if we have everything necessary for JIT +## CLANG_ROOT Path to prefix of clang installation +## CLANG_EXECUTABLE Path to clang++ +## CLANG_GCC_INSTALLATION Path to the GGC installation that clang is selecting +## CLANG_RESOURCE_DIR Path to clang's resource directory (can be set to override) +## LLVM_ROOT Path to prefix of LLVM installation +## CLANG_VERSION Version of clang we'll be using for JIT +## LLVM_VERSION Version of LLVM we'll be usign for JIT + +# These are the minimum version for JIT support, not for compiling the HILTI/Spicy. + +# 9.0 works +# 10.x is untested +set(llvm_mininum_version "3.0.0") +set(clang_mininum_version "3.0.0") + +# The GCC toolchain is important at runtime, and supports lower versions compared +# to when we use GCC to compile HILTI/Spicy itself. +# +# 7 seems to work. +# 8 is untested. +# 9 is untested. +set(gcc_toolchain_minimum_version "7") + +macro(error msg) + message(STATUS "Warning: ${msg}") + set(CLANG_JIT_FOUND no) +endmacro() + +set(CLANG_JIT_FOUND yes) + +if ( NOT CLANG_ROOT AND NOT LLVM_ROOT ) + # If our standard compiler is clang, we prefer an LLVM that's at the + # same place. So we look for llvm-config inside the same directory as + # clang itself and ask it for its paths. + if ( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) + string(REPLACE "/clang++" "/llvm-config" LLVM_CONFIG "${CMAKE_CXX_COMPILER}") + if ( EXISTS "${LLVM_CONFIG}" ) + execute_process(COMMAND ${LLVM_CONFIG} --prefix OUTPUT_VARIABLE LLVM_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE) + endif () + endif () +endif () + +if ( LLVM_ROOT AND NOT CLANG_ROOT ) + set(CLANG_ROOT ${LLVM_ROOT}) +endif () + +if ( CLANG_ROOT AND NOT LLVM_ROOT ) + set(LLVM_ROOT ${CLANG_ROOT}) +endif () + +# Inspired by https://lowlevelbits.org/building-an-llvm-based-tool.-lessons-learned/ + +if ( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) + # Also try the prefix where clang++ is installed (which can be different from + # what llvm-config tells us.) + get_filename_component(_cxx_compiler_prefix "${CMAKE_CXX_COMPILER}/" DIRECTORY) + get_filename_component(_cxx_compiler_prefix "${_cxx_compiler_prefix}/" DIRECTORY) +endif () + +set(SEARCH_PATHS + ${LLVM_ROOT} + ${LLVM_ROOT}/lib/cmake + ${LLVM_ROOT}/lib/cmake/llvm + ${LLVM_ROOT}/share/llvm/cmake/ + ${CLANG_ROOT} + ${CLANG_ROOT}/lib/cmake + ${CLANG_ROOT}/lib/cmake/clang + ${CLANG_ROOT}/share/clang/cmake/ + ${_cxx_compiler_prefix} +) + +set(search_default_path "") +if ( CLANG_ROOT OR LLVM_ROOT ) + set(search_default_path "NO_DEFAULT_PATH") +endif () + +find_package(LLVM CONFIG PATHS ${SEARCH_PATHS} ${search_default_path}) +find_package(Clang CONFIG PATHS ${SEARCH_PATHS} ${search_default_path}) + +set(CLANG_JIT_FOUND yes) + +if ( NOT LLVM_INSTALL_PREFIX ) + set(LLVM_ROOT "not found") + error("Did not find LLVM installation") +endif () + +if ( CLANG_INSTALL_PREFIX ) + file(STRINGS ${CLANG_INSTALL_PREFIX}/include//clang/Basic/Version.inc CLANG_VERSION + REGEX " CLANG_VERSION " + LIMIT_COUNT 1) + separate_arguments(CLANG_VERSION) + list(GET CLANG_VERSION 2 CLANG_VERSION) +else () + set(CLANG_ROOT "not found") + error("Did not find Clang installation") +endif () + +require_version("LLVM" LLVM_FOUND LLVM_VERSION ${llvm_mininum_version} false) +require_version("Clang" CLANG_FOUND CLANG_VERSION ${clang_mininum_version} false) + +set(CLANG_EXECUTABLE "n/a") +set(CLANG_GCC_INSTALLATION "n/a") +set(CLANG_ROOT "${CLANG_INSTALL_PREFIX}") +set(LLVM_ROOT "${LLVM_INSTALL_PREFIX}") + +if ( CLANG_JIT_FOUND ) + if ( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) + set(CLANG_EXECUTABLE ${CMAKE_CXX_COMPILER}) + else () + set(CLANG_EXECUTABLE "${CLANG_ROOT}/bin/clang++") + endif() + + if ( EXISTS ${CLANG_EXECUTABLE} ) + execute_process(COMMAND /bin/sh "-c" "${CLANG_EXECUTABLE} -v 2>&1 | awk '/Selected GCC installation/ { print $NF; }'" + OUTPUT_VARIABLE CLANG_GCC_INSTALLATION + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if ( NOT CLANG_RESOURCE_DIR ) + execute_process(COMMAND ${CLANG_EXECUTABLE} -print-resource-dir + OUTPUT_VARIABLE CLANG_RESOURCE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif () + + if ( CLANG_GCC_INSTALLATION ) + string(REGEX REPLACE "^.*/([0-9])$" "\\1" toolchain_version ${CLANG_GCC_INSTALLATION}) + if ( NOT toolchain_version OR ${toolchain_version} LESS ${gcc_toolchain_minimum_version} ) + message(STATUS "Warning: GCC toolchain version must be at least " + "${gcc_toolchain_minimum_version} for JIT support, detected: " + "${${toolchain_version}}") + set(CLANG_JIT_FOUND no) + endif () + else () + set(CLANG_GCC_INSTALLATION "n/a") + endif () + + if ( NOT CLANG_RESOURCE_DIR ) + set(CLANG_RESOURCE_DIR "not found") + error("Could not determine clang's resource directory") + endif () + + else () + set(CLANG_EXECUTABLE "not found") + set(CLANG_RESOURCE_DIR "not found") + error("Could not determine path of clang++") + endif () + + if ( LLVM_LINK_LLVM_DYLIB ) + # If there's a shared LLVM lib, we use that because the clang libraries might be doing the same + # and we'd get duplicated symbols otherwise. Note that the clang-cpp shared library always + # exists since clang 9. + set(llvm_libs LLVM) + set(clang_libs clang-cpp) + else () + set(llvm_libs LLVMOrcJIT LLVMX86AsmParser LLVMX86CodeGen) + set(clang_libs clangFrontend clangCodeGen) + endif () + + message(STATUS "Linking against LLVM libraries '${llvm_libs}'") + message(STATUS "Linking against clang libraries '${clang_libs}'") + + add_library(clang-jit INTERFACE) + target_include_directories(clang-jit INTERFACE ${LLVM_INCLUDE_DIRS}) + target_compile_definitions(clang-jit INTERFACE ${LLVM_DEFINITIONS}) + target_link_options(clang-jit INTERFACE "LINKER:-rpath;${LLVM_LIBRARY_DIR}") + target_link_libraries(clang-jit INTERFACE ${llvm_libs} ${clang_libs}) +endif () diff --git a/cmake/FindFilesystem.cmake b/cmake/FindFilesystem.cmake new file mode 100644 index 000000000..b8362ada5 --- /dev/null +++ b/cmake/FindFilesystem.cmake @@ -0,0 +1,232 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. +# +# From: https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake +# Version 9ce8ea3 + +#[=======================================================================[.rst: + +FindFilesystem +############## + +This module supports the C++17 standard library's filesystem utilities. Use the +:imp-target:`std::filesystem` imported target to + +Options +******* + +The ``COMPONENTS`` argument to this module supports the following values: + +.. find-component:: Experimental + :name: fs.Experimental + + Allows the module to find the "experimental" Filesystem TS version of the + Filesystem library. This is the library that should be used with the + ``std::experimental::filesystem`` namespace. + +.. find-component:: Final + :name: fs.Final + + Finds the final C++17 standard version of the filesystem library. + +If no components are provided, behaves as if the +:find-component:`fs.Final` component was specified. + +If both :find-component:`fs.Experimental` and :find-component:`fs.Final` are +provided, first looks for ``Final``, and falls back to ``Experimental`` in case +of failure. If ``Final`` is found, :imp-target:`std::filesystem` and all +:ref:`variables ` will refer to the ``Final`` version. + + +Imported Targets +**************** + +.. imp-target:: std::filesystem + + The ``std::filesystem`` imported target is defined when any requested + version of the C++ filesystem library has been found, whether it is + *Experimental* or *Final*. + + If no version of the filesystem library is available, this target will not + be defined. + + .. note:: + This target has ``cxx_std_17`` as an ``INTERFACE`` + :ref:`compile language standard feature `. Linking + to this target will automatically enable C++17 if no later standard + version is already required on the linking target. + + +.. _fs.variables: + +Variables +********* + +.. variable:: CXX_FILESYSTEM_IS_EXPERIMENTAL + + Set to ``TRUE`` when the :find-component:`fs.Experimental` version of C++ + filesystem library was found, otherwise ``FALSE``. + +.. variable:: CXX_FILESYSTEM_HAVE_FS + + Set to ``TRUE`` when a filesystem header was found. + +.. variable:: CXX_FILESYSTEM_HEADER + + Set to either ``filesystem`` or ``experimental/filesystem`` depending on + whether :find-component:`fs.Final` or :find-component:`fs.Experimental` was + found. + +.. variable:: CXX_FILESYSTEM_NAMESPACE + + Set to either ``std::filesystem`` or ``std::experimental::filesystem`` + depending on whether :find-component:`fs.Final` or + :find-component:`fs.Experimental` was found. + + +Examples +******** + +Using `find_package(Filesystem)` with no component arguments: + +.. code-block:: cmake + + find_package(Filesystem REQUIRED) + + add_executable(my-program main.cpp) + target_link_libraries(my-program PRIVATE std::filesystem) + + +#]=======================================================================] + + +if(TARGET std::filesystem) + # This module has already been processed. Don't do it again. + return() +endif() + +include(CMakePushCheckState) +include(CheckIncludeFileCXX) +include(CheckCXXSourceCompiles) + +cmake_push_check_state() + +set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY}) + +# All of our tests required C++17 or later +set(CMAKE_CXX_STANDARD 17) + +# Normalize and check the component list we were given +set(want_components ${Filesystem_FIND_COMPONENTS}) +if(Filesystem_FIND_COMPONENTS STREQUAL "") + set(want_components Final) +endif() + +# Warn on any unrecognized components +set(extra_components ${want_components}) +list(REMOVE_ITEM extra_components Final Experimental) +foreach(component IN LISTS extra_components) + message(WARNING "Extraneous find_package component for Filesystem: ${component}") +endforeach() + +# Detect which of Experimental and Final we should look for +set(find_experimental TRUE) +set(find_final TRUE) +if(NOT "Final" IN_LIST want_components) + set(find_final FALSE) +endif() +if(NOT "Experimental" IN_LIST want_components) + set(find_experimental FALSE) +endif() + +if(find_final) + check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER) + mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER) + if(_CXX_FILESYSTEM_HAVE_HEADER) + # We found the non-experimental header. Don't bother looking for the + # experimental one. + set(find_experimental FALSE) + endif() +else() + set(_CXX_FILESYSTEM_HAVE_HEADER FALSE) +endif() + +if(find_experimental) + check_include_file_cxx("experimental/filesystem" _CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) + mark_as_advanced(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) +else() + set(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER FALSE) +endif() + +if(_CXX_FILESYSTEM_HAVE_HEADER) + set(_have_fs TRUE) + set(_fs_header filesystem) + set(_fs_namespace std::filesystem) + set(CXX_FILESYSTEM_IS_EXPERIMENTAL false) +elseif(_CXX_FILESYSTEM_HAVE_EXPERIMENTAL_HEADER) + set(_have_fs TRUE) + set(_fs_header experimental/filesystem) + set(_fs_namespace std::experimental::filesystem) + set(CXX_FILESYSTEM_IS_EXPERIMENTAL true) +else() + set(_have_fs FALSE) +endif() + +set(CXX_FILESYSTEM_HAVE_FS ${_have_fs} CACHE BOOL "TRUE if we have the C++ filesystem headers") +set(CXX_FILESYSTEM_HEADER ${_fs_header} CACHE STRING "The header that should be included to obtain the filesystem APIs") +set(CXX_FILESYSTEM_NAMESPACE ${_fs_namespace} CACHE STRING "The C++ namespace that contains the filesystem APIs") + +set(_found FALSE) + +if(CXX_FILESYSTEM_HAVE_FS) + # We have some filesystem library available. Do link checks + string(CONFIGURE [[ + #include <@CXX_FILESYSTEM_HEADER@> + + int main() { + auto cwd = @CXX_FILESYSTEM_NAMESPACE@::current_path(); + return static_cast(cwd.string().size()); + } + ]] code @ONLY) + + # Try to compile a simple filesystem program without any linker flags + check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) + + set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED}) + + if(NOT CXX_FILESYSTEM_NO_LINK_NEEDED) + set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) + # Add the libstdc++ flag + set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) + check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED) + set(can_link ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) + if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED) + # Try the libc++ flag + set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) + check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED) + set(can_link ${CXX_FILESYSTEM_CPPFS_NEEDED}) + endif() + endif() + + if(can_link) + add_library(std::filesystem INTERFACE IMPORTED) + target_compile_features(std::filesystem INTERFACE cxx_std_17) + set(_found TRUE) + + if(CXX_FILESYSTEM_NO_LINK_NEEDED) + # Nothing to add... + elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED) + target_link_libraries(std::filesystem INTERFACE -lstdc++fs) + elseif(CXX_FILESYSTEM_CPPFS_NEEDED) + target_link_libraries(std::filesystem INTERFACE -lc++fs) + endif() + endif() +endif() + +cmake_pop_check_state() + +set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can compile and link a program using std::filesystem" FORCE) + +if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND) + message(FATAL_ERROR "Cannot Compile simple program using std::filesystem") +endif() diff --git a/cmake/FindGoldLinker.cmake b/cmake/FindGoldLinker.cmake new file mode 100644 index 000000000..3a86d2e55 --- /dev/null +++ b/cmake/FindGoldLinker.cmake @@ -0,0 +1,22 @@ + +## Enable Gold linker if available. + +set(GOLD_FOULD "no") + +if ( USE_GOLD ) + if ( UNIX AND NOT APPLE AND NOT LLD_PATH ) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE ld_version) + if ("${ld_version}" MATCHES "GNU gold") + message(STATUS "Using Gold linker") + set(GOLD_FOUND "yes") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold") + else () + message(STATUS "Gold linker not available") + endif () + endif () +else () + message(STATUS "Gold linker usage disabled") +endif () + +set(GOLD_FOUND ${GOLD_FOUND} CACHE BOOL "TRUE if we activated the Linux Gold linker") diff --git a/cmake/FindZeek.cmake b/cmake/FindZeek.cmake new file mode 100644 index 000000000..0fdf395b7 --- /dev/null +++ b/cmake/FindZeek.cmake @@ -0,0 +1,82 @@ +# Find Zeek installation. +# +# Variables used by this module that can be set before calling find_package: +# +# ZEEK_ROOT_DIR Set this variable to the root installation of Zeek if the +# module has problems finding the proper installation path. +# +# Variables defined by this module: +# +# ZEEK_FOUND Zeek is installed. +# ZEEK_CONFIG Path to Zeek configuration. +# ZEEK_CXX_FLAGS C++ flags to compile a Zeek plugin. +# ZEEK_CMAKE_DIR Path to Zeek's CMake files. +# ZEEK_INCLUDE_DIR Path to Zeek's plugin directory. +# ZEEK_PLUGIN_DIR Path to Zeek's plugin directory. +# ZEEK_PREFIX Path to Zeek's installation prefix. +# ZEEK_VERSION Version of Zeek. +# ZEEK_DEBUG_BUILD True if Zeek was build in debug mode +# ZEEK_EXE Path to zeek executale +# BifCl_EXE Path to bifcl +# +# Interface target to link to a Zeek plugin: +# +# Zeek::Zeek + +set(zeek_mininum_version "3.0.0") + +find_program(ZEEK_CONFIG zeek-config + HINTS ${ZEEK_ROOT_DIR}/bin /usr/local/zeek/bin) + +if ( ZEEK_CONFIG ) + message(STATUS "Found zeek-config at ${ZEEK_CONFIG}") + execute_process(COMMAND "${ZEEK_CONFIG}" --include_dir + OUTPUT_VARIABLE ZEEK_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(ZEEK_CXX_FLAGS "-I${ZEEK_INCLUDE_DIR}") + + execute_process(COMMAND "${ZEEK_CONFIG}" --cmake_dir + OUTPUT_VARIABLE ZEEK_CMAKE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND "${ZEEK_CONFIG}" --prefix + OUTPUT_VARIABLE ZEEK_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND "${ZEEK_CONFIG}" --plugin_dir + OUTPUT_VARIABLE ZEEK_PLUGIN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND "${ZEEK_CONFIG}" --version + OUTPUT_VARIABLE ZEEK_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND "${ZEEK_CONFIG}" --build_type + OUTPUT_VARIABLE ZEEK_DEBUG_BUILD + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if ( "${ZEEK_DEBUG_BUILD}" STREQUAL "debug" ) + set(ZEEK_DEBUG_BUILD yes) + else () + set(ZEEK_DEBUG_BUILD no) + endif () + + find_program(BifCl_EXE bifcl HINTS ${ZEEK_PREFIX}/bin NO_DEFAULT_PATH) + find_program(ZEEK_EXE zeek HINTS ${ZEEK_PREFIX}/bin NO_DEFAULT_PATH) +else () + set(ZEEK_VERSION "not found") + set(ZEEK_PREFIX "not found") + set(ZEEK_EXE "not found") +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Zeek DEFAULT_MSG ZEEK_CONFIG) + +require_version("Zeek" ZEEK_FOUND ZEEK_VERSION ${zeek_mininum_version} false) + +if ( ZEEK_FOUND ) + set(ZEEK_FOUND "yes" CACHE BOOL "Have Zeek's Spicy plugin" FORCE) +else () + set(ZEEK_FOUND "no" CACHE BOOL "Have Zeek's Spicy plugin" FORCE) +endif () diff --git a/cmake/TypeErase.cmake b/cmake/TypeErase.cmake new file mode 100644 index 000000000..d8eed2a53 --- /dev/null +++ b/cmake/TypeErase.cmake @@ -0,0 +1,39 @@ + +# TODO: Clean this up. Turn into functions with named parameters. + +### Generate the type-erased classes. +### +### If "is_constant" is true, the generated code assumes that instances can't be modified +### once created and will not deep-copy them on assignment. +macro(autogen_type_erased outputs api is_constant) + get_filename_component(_output ${api} NAME_WE) + set(_output "${AUTOGEN_H}/__${_output}.h") + + if ( ${is_constant} ) + set(_const_arg "--constant") + else() + set(_const_arg "") + endif() + + add_custom_command( + COMMAND ${CMAKE_SOURCE_DIR}/scripts/autogen-type-erased ${_const_arg} --output ${_output} ${CMAKE_CURRENT_SOURCE_DIR}/${api} + DEPENDS ${CMAKE_SOURCE_DIR}/scripts/autogen-type-erased ${CMAKE_CURRENT_SOURCE_DIR}/${api} + OUTPUT ${_output} + ) + + set_source_files_properties(${_output} PROPERTIES GENERATED TRUE) + list(APPEND ${outputs} ${_output}) +endmacro() + +### Merge nodes.decl files into dispatcher code. +macro(autogen_dispatchers outputs dst) + set(_output "${dst}") + add_custom_command( + COMMAND ${CMAKE_SOURCE_DIR}/scripts/autogen-dispatchers --output ${_output} ${ARGN} + DEPENDS ${CMAKE_SOURCE_DIR}/scripts/autogen-dispatchers ${ARGN} + OUTPUT ${_output} + ) + + set_source_files_properties(${_output} PROPERTIES GENERATED TRUE) + list(APPEND ${outputs} ${_output}) +endmacro() diff --git a/cmake/Util.cmake b/cmake/Util.cmake new file mode 100644 index 000000000..ca2cf24d6 --- /dev/null +++ b/cmake/Util.cmake @@ -0,0 +1,89 @@ +### A collection of small helpers for the HILTI/Spicy build system. + +# Add the likely Bison include directory to Bison sources to avoid version mismatches. +function(bison_source src output_dir) + string(REGEX REPLACE "/bin/bison" "/include" bison_include "${BISON_EXECUTABLE}") + set_source_files_properties(${src} PROPERTIES INCLUDE_DIRECTORIES "${bison_include};${FLEX_INCLUDE_DIR};${output_dir}") +endfunction() + +# Install a set of header files from a directory location. +function(install_headers src dst) + if ( NOT IS_ABSOLUTE "${src}" ) + set(src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") + endif () + + if ( NOT IS_ABSOLUTE "${dst}" ) + set(dst_msg "${CMAKE_INSTALL_FULL_INCLUDEDIR}/${dst}") + set(dst "${CMAKE_INSTALL_INCLUDEDIR}/${dst}") + endif () + + if ( ARGN ) + foreach ( i ${ARGN} ) + install(FILES ${src}/${i} DESTINATION ${dst}) + endforeach () + else () + install(CODE "message(STATUS \"Installing: ${dst_msg}/*\")") + + install(DIRECTORY ${src}/ + DESTINATION ${dst} + MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h" + PATTERN "*.hpp" + PATTERN "*.hh" + ) + endif () +endfunction () + +# Add a symlink at installation time. +function(install_symlink filepath sympath) + install(CODE "message(\"-- Creating symbolic link: ${sympath} -> ${filepath}\")") + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} ${sympath})") +endfunction(install_symlink) + +# Initialize a variable that'll go into a {hilti,spicy}/config.cc +# file. This performans some normalization: turn lists into +# space-separated strings and strip/reduce whitespace. +function(set_config_val dst val) + if ( NOT "${val}" STREQUAL "" ) + string(REPLACE ";" " " _x "${val}") + string(STRIP "${_x}" _x) + string(REGEX REPLACE " *" " " _x "${_x}") + set(${dst} "${_x}" PARENT_SCOPE) + else () + set(${dst} "" PARENT_SCOPE) + endif () +endfunction() + +function(make_install_rpath dst relative_from relative_to) + # The following is from "Professional CMake" to set target's RPATH relative to their location. + if ( APPLE ) + set(base_point "@loader_path") + else () + set(base_point "$ORIGIN") + endif () + + file(RELATIVE_PATH relative_lib_path ${relative_from} ${relative_to}) + set(${dst} "${base_point};${base_point}/${relative_lib_path}" PARENT_SCOPE) +endfunction () + +# Warn or abort if we don't a given version isn't recent enough. +function(require_version name found have need require) + if ( NOT ${found} ) + if ( require ) + message(FATAL_ERROR "${name} required, but not found") + else () + set(${found} no PARENT_SCOPE) + set(${have} "not found") + endif () + else () + if ( ${have} VERSION_LESS "${need}" ) + if ( require ) + message(FATAL_ERROR "Need ${name} version >= ${need}, found ${${have}}") + endif () + + message(STATUS "Warning: Need ${name} version >= ${need}, found ${${have}}") + set(${found} no PARENT_SCOPE) + set(${have} "${${have}} (error: too old, must be at least ${zeek_mininum_version})" PARENT_SCOPE) + endif() + endif() +endfunction () diff --git a/configure b/configure new file mode 100755 index 000000000..f869833d1 --- /dev/null +++ b/configure @@ -0,0 +1,192 @@ +#! /bin/sh +# +# Convenience wrapper for easily viewing/setting options that +# the project's CMake scripts will recognize +# +# Adapted from Zeek's wrapper. + +# Defaults +cmake_bison_root="" +cmake_build_directory="build" +cmake_build_shared_libs="yes" +cmake_build_type="RelWithDebInfo" +cmake_clang_root="" +cmake_c_compiler="" +cmake_cxx_compiler="" +cmake_c_system_include_dirs="" +cmake_cxx_system_include_dirs="" +cmake_clang_resource_dir="" +cmake_flex_root="" +cmake_generator="" +cmake_hilti_have_jit="yes" +cmake_install_prefix="/usr/local" +cmake_llvm_root="" +cmake_use_ccache="no" +cmake_use_gold="yes" +cmake_use_sanitizers="" +cmake_use_werror="false" +cmake_zeek_root_dir="/usr/local/zeek" + +display_cmake=0 +cache_entries="" + +set -e +command="$0 $*" + +cmake_exe="" +for i in cmake cmake3; do + if command -v $i >/dev/null; then + $i --version | grep -q "cmake.*version 3" && cmake_exe=$(which $i) + break + fi +done + +type ${cmake_exe} > /dev/null 2>&1 || { + echo "\ +This package requires CMake, please install it first, then you may +use this configure script to access CMake equivalent functionality.\ +" >&2; + exit 1; +} + +usage="\ +Usage: $0 [OPTION]... [VAR=VALUE]... + + Build Options: + --build-static-libs Build static libraries instead [default: shared] + --build-dir=DIR Place build files in directory [default: ${cmake_build_directory}] + --build-type=TYPE Set build type (Debug,Release,RelWithDebInfo) [default: ${cmake_build_type}] + --disable-jit Do not compile in support for JIT compilation + --disable-gold On Linux, do not try to use the gold linker + --enable-ccache Build using the compiler cache cache if in PATH [default: ${cmake_use_ccache}] + --enable-debug Compile debug version (same as --build-type=Debug) [default: off] + --enable-sanitizer[=] Enable sanitizer(s), default if not further specified is \"address\" + --enable-werror Treat compiler warnings as errors [default: ${cmake_use_werror}] + --generator= CMake generator to use (see cmake --help) + --prefix=PATH Installation prefix [default: ${cmake_install_prefix}] + --with-bison= Set prefix of Bison installation + --with-clang-root= Set prefix of Clang installation + --with-c-compiler= Set C compiler to use + --with-cxx-compiler= Set C++ compiler to use + --with-flex= Set prefix of Flex installation + --with-libc-include-dirs= Set additional include directories for C library headers during JIT (separate with colons) + --with-libcxx-include-dirs= Set additional include directories for C++ library headers during JIT (separate with colons) + --with-clang-resource-dir= Set resource directory to use with clang during JIT + --with-llvm-root= Set prefix of LLVM installation + --with-zeek=PATH Path to Zeek installation [default: ${cmake_zeek_root_dir}] + + --display-cmake Don't create build configuration, just output final CMake invocation +" +source_dir="$(cd "$(dirname "$0")" && pwd)" + +# Function to append a CMake cache entry definition to the +# cmake_cache_entries variable. +# $1 is the cache entry variable name +# $2 is the cache entry variable type +# $3 is the cache entry variable value +append_cache_entry () { + if [ "$3" != "" ]; then + cmake_cache_entries="${cmake_cache_entries} -D $1:$2=$3" + fi +} + +# parse arguments +while [ $# -ne 0 ]; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case "$1" in + --build-static-libs) cmake_build_shared_libs="false";; + --build-dir=*|--builddir=*) cmake_build_directory="${optarg}";; + --build-type=*) cmake_build_type="${optarg}";; + --disable-jit) cmake_hilti_have_jit="no";; + --disable-gold) cmake_use_gold="no";; + --enable-ccache) cmake_use_ccache="yes";; + --enable-debug) cmake_build_type="Debug";; + --enable-sanitizer) cmake_use_sanitizers="address";; + --enable-sanitizer=*) cmake_use_sanitizers="${optargs}";; + --enable-werror) cmake_use_werror="yes";; + --disable-jit) cmake_hilti_have_jit="no";; + --generator=*) cmake_generator="${optarg}";; + --prefix=*) cmake_install_prefix="${optarg}";; + --with-clang-root=*) cmake_clang_root="${optarg}";; + --with-c-compiler=*) cmake_c_compiler="${optarg}";; + --with-cxx-compiler=*) + cmake_cxx_compiler="${optarg}" + + if [ -z "${cmake_c_compiler}" ]; then + try_c_compiler=$(echo "${cmake_cxx_compiler}" | sed 's/++//g') + command -v "${try_c_compiler}" >/dev/null && cmake_c_compiler="${try_c_compiler}" + fi + ;; + + --with-c-system-include-dirs=*) cmake_c_system_include_dirs="${optarg}";; + --with-cxx-system-include-dirs=*) cmake_cxx_system_include_dirs="${optarg}";; + --with-clang-resource-dir=*) cmake_clang_resource_dir="${optarg}";; + --with-flex=*) cmake_flex_root="${optarg}";; + --with-bison=*) cmake_bison_root="${optarg}";; + --with-llvm-root=*) cmake_llvm_root="${optarg}";; + --with-zeek=*) cmake_zeek_root_dir="${optarg}";; + --without-bison=*) cmake_bison_root="";; + --without-clang-root=*) cmake_clang_root=""; cmake_hilti_have_jit="no";; + --without-flex=*) cmake_flex_root="";; + --without-llvm-root=*) cmake_llvm_root=""; cmake_hilti_have_jit="no";; + --without-zeek) cmake_zeek_root_dir="";; + + --display-cmake) display_cmake=1;; + + --help|-h) echo "${usage}" 1>&2 && exit 1;; + *) echo "Invalid option '$1'. Try $0 --help to see available options." && exit 1;; + esac + shift +done + +# Set CMake cache options. +append_cache_entry BISON_ROOT PATH "${cmake_bison_root}" +append_cache_entry BUILD_SHARED_LIBS BOOL "${cmake_build_shared_libs}" +append_cache_entry CLANG_ROOT PATH "${cmake_clang_root}" +append_cache_entry CMAKE_BUILD_TYPE STRING "${cmake_build_type}" +append_cache_entry CMAKE_C_COMPILER PATH "${cmake_c_compiler}" +append_cache_entry CMAKE_CXX_COMPILER PATH "${cmake_cxx_compiler}" +append_cache_entry CLANG_RESOURCE_DIR PATH "${cmake_clang_resource_dir}" +append_cache_entry C_SYSTEM_INCLUDE_DIRS PATH "${cmake_c_system_include_dirs}" +append_cache_entry CXX_SYSTEM_INCLUDE_DIRS PATH "${cmake_cxx_system_include_dirs}" +append_cache_entry CMAKE_INSTALL_PREFIX PATH "${cmake_install_prefix}" +append_cache_entry FLEX_ROOT PATH "${cmake_flex_root}" +append_cache_entry HILTI_HAVE_JIT BOOL "${cmake_hilti_have_jit}" +append_cache_entry LLVM_ROOT PATH "${cmake_llvm_root}" +append_cache_entry USE_CCACHE BOOL "${cmake_use_ccache}" +append_cache_entry USE_GOLD BOOL "${cmake_use_gold}" +append_cache_entry USE_SANITIZERS STRING "${cmake_use_sanitizers}" +append_cache_entry USE_WERROR BOOL "${cmake_use_werror}" +append_cache_entry ZEEK_ROOT_DIR PATH "${cmake_zeek_root_dir}" + +if [ -n "${cmake_generator}" ]; then + cmake="${cmake_exe} -G '${cmake_generator}' ${cmake_cache_entries} ${source_dir}" +else + cmake="${cmake_exe} ${cmake_cache_entries} ${source_dir}" +fi + +if [ "${display_cmake}" = 1 ]; then + echo "${cmake}" + exit 0 +fi + +if [ ! -d ${cmake_build_directory} ]; then + # Create build directory + mkdir -p ${cmake_build_directory} +else + # If build directory already exists, remove any pre-existing + # CMake cache so that this configuration is not tainted by a + # previous one + rm -f ${cmake_build_directory}/CMakeCache.txt +fi + +cd ${cmake_build_directory} +eval ${cmake} 2>&1 | tee config.log + +echo "# This is the command used to configure this build" > config.status +echo ${command} >> config.status +chmod u+x config.status diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..24600083d --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +!Makefile diff --git a/doc/CHANGES.rst b/doc/CHANGES.rst new file mode 100644 index 000000000..0ee88370d --- /dev/null +++ b/doc/CHANGES.rst @@ -0,0 +1,2 @@ + + diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..d53f17911 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,56 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -W --keep-going -n +SPHINXBUILD = sphinx-build +SPHINXPROJ = Spicy +BUILDDIR = $(PWD)/../build +SOURCEDIR = $(BUILDDIR)/doc-root +DESTDIR = $(BUILDDIR)/html + +AUTOGEN = $(BUILDDIR)/html/autogen +SPICYDOC = $(BUILDDIR)/bin/spicy-doc +SPICYDTR = ./scripts/spicy-doc-to-rst + +# Include these namespaces into the autogenerated type reference. +SPICY_TYPES = address,bitfield,bool,bytes,enum,exception,integer,list,port,real,regexp,sink,stream,string,struct,tuple,unit,vector + +all: html + +spicy-lib: $(AUTOGEN)/spicy-functions.spicy $(AUTOGEN)/spicy-types.spicy $(AUTOGEN)/filter-types.spicy $(AUTOGEN)/zeek-functions.spicy + +$(AUTOGEN)/spicy-functions.spicy : ../spicy/lib/spicy.spicy scripts/autogen-spicy-lib Makefile + scripts/autogen-spicy-lib functions spicy <../spicy/lib/spicy.spicy >$(AUTOGEN)/spicy-functions.spicy + +$(AUTOGEN)/spicy-types.spicy : ../spicy/lib/spicy.spicy scripts/autogen-spicy-lib Makefile + scripts/autogen-spicy-lib types spicy <../spicy/lib/spicy.spicy >$(AUTOGEN)/spicy-types.spicy + +$(AUTOGEN)/filter-types.spicy : ../spicy/lib/filter.spicy scripts/autogen-spicy-lib Makefile + scripts/autogen-spicy-lib types spicy <../spicy/lib/filter.spicy >$(AUTOGEN)/filter-types.spicy + +$(AUTOGEN)/zeek-functions.spicy : ../zeek/plugin/spicy/zeek.spicy scripts/autogen-spicy-lib Makefile + scripts/autogen-spicy-lib functions zeek <../zeek/plugin/spicy/zeek.spicy >$(AUTOGEN)/zeek-functions.spicy + +update-autogen: clean + @UPDATE_SPICY_CODE=1 $(MAKE) + +update-operators: + rm -f $(AUTOGEN)/.timestamp && $(MAKE) $(AUTOGEN)/.timestamp + +clean: + rm -rf $(DESTDIR) + rm -rf $(AUTOGEN) + +html: Makefile $(AUTOGEN)/.timestamp spicy-lib + @rm -f "$(BUILDDIR)/doc-root" && ln -s "../doc" "$(BUILDDIR)/doc-root" + @ # The RTD theme might be producing RemovedInSphinx30Warning + @PYTHONWARNINGS="ignore" SPICY_BUILD_DIRECTORY=$(BUILDDIR) $(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(DESTDIR)" $(SPHINXOPTS) $(O) + @echo Built documentation in $(DESTDIR)/html/index.html + +$(AUTOGEN)/.timestamp: $(SPICYDOC) $(SPICYDTR) + @echo Auto-generating reference documentation ... + @$(SPICYDOC) | $(SPICYDTR) -t $(SPICY_TYPES) -d $(AUTOGEN)/types + @touch $(AUTOGEN)/.timestamp + +.PHONY: help Makefile diff --git a/doc/_static/Spicy-Architecture.svg b/doc/_static/Spicy-Architecture.svg new file mode 100644 index 000000000..a1cb08e63 --- /dev/null +++ b/doc/_static/Spicy-Architecture.svg @@ -0,0 +1,3 @@ +Original ASTBuild scopesResolve IDsResolve operatorsTransformFinal ASTValidate*.spicy*.hlthilti::detail::parser::DriverImport moduleshilti::Driverspicy::detail::CodeGenhilti::detail::buildScopes()spicy::detail::CodeGen::compileUnit()spicy::detail::codegen:: GrammarBuilderspicy::detail::codegen:: ParserBuilderhilti::detail::CodeGenhilti::JITcxx::Unitcxx::CompiledUnitIterate  +until  +stableApply Coercionshilti::Unit::compile()spicy::detail::buildScopes()spicy::detail::resolveIDs()hilti::detail::resolveIDs()spicy::detail::resolveOperators(hilti::detail::resolveOperators(spicy::detail::applyCoercions()hilti::detail::applyCoercions()spicy::detail::validate()hilti::detail::validate(){hilti,spicy}/src/compiler/visitors/*spicy::detail::printAST()hilti::detail::printAST()*.hlt*.spicyspicy::detail::parser::Drivercxx::Unit...hilti::unit::link()cxx::UnitSystem LinkerFinal Executable Codecxx::UnitHILTI Runtime LibrarySpicy Runtime LibraryClassInput/OutputFunctionProcessing Stephilti::detail::coerce{Ctor/Type}()spicy::detail::coerce{Ctor/Type}()Legendspicy/include/compiler/detail/codegen/*{hilti,spicy}/src/compiler/visitors/coercer.cchilti::detail::coerceOperands(){hilti,spicy}/include/compiler/detail/parser/*hilti/include/compiler/detail/codegen/*hilti/include/compiler/jit.hhilti/{include,src}/rt/*spicy/{include,src}/rt/*hilti/include/compiler/driver.h \ No newline at end of file diff --git a/doc/_static/hilti-favicon.ico b/doc/_static/hilti-favicon.ico new file mode 100644 index 000000000..031c21f7e Binary files /dev/null and b/doc/_static/hilti-favicon.ico differ diff --git a/doc/_static/hilti-logo.png b/doc/_static/hilti-logo.png new file mode 100644 index 000000000..f812e6b9b Binary files /dev/null and b/doc/_static/hilti-logo.png differ diff --git a/doc/_static/spicy-favicon.ico b/doc/_static/spicy-favicon.ico new file mode 100644 index 000000000..2177d6ca5 Binary files /dev/null and b/doc/_static/spicy-favicon.ico differ diff --git a/doc/_static/spicy-logo-square.png b/doc/_static/spicy-logo-square.png new file mode 100644 index 000000000..68dc45d45 Binary files /dev/null and b/doc/_static/spicy-logo-square.png differ diff --git a/doc/_static/spicy-logo-with-border.png b/doc/_static/spicy-logo-with-border.png new file mode 100644 index 000000000..a28c4ec94 Binary files /dev/null and b/doc/_static/spicy-logo-with-border.png differ diff --git a/doc/_static/spicy-logo-wth-border.png b/doc/_static/spicy-logo-wth-border.png new file mode 100644 index 000000000..a28c4ec94 Binary files /dev/null and b/doc/_static/spicy-logo-wth-border.png differ diff --git a/doc/_static/spicy-logo.png b/doc/_static/spicy-logo.png new file mode 100644 index 000000000..1dabe0704 Binary files /dev/null and b/doc/_static/spicy-logo.png differ diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html new file mode 100644 index 000000000..3cabe83ff --- /dev/null +++ b/doc/_templates/layout.html @@ -0,0 +1,9 @@ +{% extends '!layout.html' %} + +{% block sidebartitle %} + +{{super()}} + + GitHub + +{% endblock %} diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000..5c8d42c43 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +import subprocess + +sys.path.insert(0, os.path.abspath('scripts')) + +os.environ["PATH"] = "%s/bin:%s" % (os.environ["SPICY_BUILD_DIRECTORY"], os.environ["PATH"]) + +# -- Project information ----------------------------------------------------- + +project = u'Spicy' +copyright = u'2020 by the Zeek Project' +author = u'Zeek Project' + +version = subprocess.check_output("../scripts/autogen-version").decode("utf8") +release = version + +# -- General configuration --------------------------------------------------- + +needs_sphinx = '1.8' + +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.extlinks', + 'spicy' +] + +exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store', '_build/autogen/types', '3rdparty/*', "_old-content"] + +templates_path = ['_templates'] + +source_suffix = '.rst' +master_doc = 'index' +pygments_style = 'sphinx' + +# Todo extension +todo_include_todos = True + +# Extlinks extension +extlinks = { + "repo": ("https://github.com/zeek/spicy/blob/master/%s", "#"), + "issue": ("https://github.com/zeek/spicy/issues/issues/%s", "#"), + "pr": ("https://github.com/zeek/spicy/pulls/%s", "#"), +} + +# -- Options for HTML output ------------------------------------------------- + +html_theme = "sphinx_rtd_theme" +html_logo = "_static/spicy-logo.png" +html_favicon = "_static/spicy-favicon.ico" +html_title = "Spicy v" + version +html_static_path = ['_static'] + +html_theme_options = { + "style_external_links": True +} diff --git a/doc/development/architecture.rst b/doc/development/architecture.rst new file mode 100644 index 000000000..3048d6ec4 --- /dev/null +++ b/doc/development/architecture.rst @@ -0,0 +1,7 @@ + +.. _dev_architecture: + +Architecture +============ + +.. image:: /_static/Spicy-Architecture.svg diff --git a/doc/development/coding-style.rst b/doc/development/coding-style.rst new file mode 100644 index 000000000..07cfcb0d9 --- /dev/null +++ b/doc/development/coding-style.rst @@ -0,0 +1,123 @@ + +.. _coding_style: + +Style +===== + +.. todo:: This is very preliminary. We'll extend it over time. It's + also not consistently applied everywhere yet. Working on that. + +Commit Messages +--------------- + +- Provide meaningful commit messages. Start the commit message with a + one line summary and then explain at a high-level what's going on, + including in particular any new functionality and changes to + existing semantics. Include short examples of functionality if + possibles. (Expect people to read your commit messages. :) + +- If the commit refers to ticket or PR, include the number into the + commit message. + +- Aim to make commits self-containing chunks of functionality. Rebase + and squash before pushing upstream. + +- Avoid rebases once pushed upstream (including in your own branches). + Just create a new commit if you need to fix something, we can squash + on merge if helpful. + +.. _clang_format: + +Formatting +---------- + +Spicy comes with a ``clang-format`` configuration that enforces a +canonical style. We require ``clang-format`` version >= 10 because we +need a style option that wasn't available earlier. There's a +``format`` target in the top-level ``Makefile`` that checks all +relevant source files for conformance with our style. A second target +``format-fixit`` reformats Spicy's code where it doesn't confirm. + +To ensure the right ``clang-format`` version is being used, either +have it in your ``PATH`` or set the environment variable +``CLANG_FORMAT`` to the full path of the binary before running either +of these targets. + +Spicy's CI runs ``make format`` as part of its code checks and will +abort if there's anything not formatted as expected. + +.. _clang_tidy: + +Linting +------- + +Spicy also comes with a ``clang-tidy`` configuration, as well as +``Makefile`` targets similar to the ones for formatting: ``make tidy`` +will check the code, and ``make tidy-fixit`` will apply fixes +automatically where ``clang-tidy`` is able to. Note that the latter +can sometimes make things worse: Double-check ``git diff`` before +committing anything. + +You can set the environment variable ``CLANG_TIDY`` to the full path +of the ``clang-tidy`` to ensure the right version is found (which, +similar to ``clang-format``, needs to be from Clang >= 10). + +Spicy's CI runs ``make tidy`` as part of its code checks and will +abort if there's anything not formatted as expected. + +Code Conventions +---------------- + +.. rubric:: Identifiers + +- Class methods: ``lowerCamelCase()`` for public and protected methods; + ``_lowerCamelCase()`` for private methods. + +- Class member constants & variables: ``lower_case`` for public + members, and ``_lower_case_with_leading_underscore`` for private + members. + +- Global function: ``lowerCamelCase()`` + +.. rubric:: Comments + +- In header files: + + - Public namespace (i.e., anything *not* in ``*::detail::*``) + + * Add Doxygen comments to all namespace elements. + + * Add Doxygen comments to all ``public`` and ``protected`` + members of classes. (Exceptions: Default constructors; + destructors; ``default`` operators; "obvious" operators, such + as basic constructors and straight-forward comparisons; + obvious getters/setters). + + - Private namespace (i.e., anything in ``*::detail::*``) + + * Add a brief sentence or two to all namespace elements that + aren't obvious. + + * Add a brief sentence or two to all class members that aren't + obvious. + +- In implementation files + + - For elements that aren't declared in a separate header file, + follow the rules for headers defining elements of the private + namespace. + + - Inside methods and functions, comment liberally but not + needlessly. Briefly explain the main reasoning behind + non-obvious logic, and introduce separate parts inside larger + chunks of code. + +.. rubric:: Doxygen style + +* Always start with a brief one-sentence summary in active voice + ("Changes X to Y.") + +* For functions and methods, include ``@param`` and ``@return`` tags + even if it seems obvious what's going on. Add ``@throws`` if the + function/method raises an exception in a way that's considered part + of its specific semantics. diff --git a/doc/development/debugging.rst b/doc/development/debugging.rst new file mode 100644 index 000000000..a04c53586 --- /dev/null +++ b/doc/development/debugging.rst @@ -0,0 +1,80 @@ + +.. _dev_debugging: + +Debugging +========= + +The user manual's :ref:`debugging section ` serves as a +good starting point for development-side debugging as well---it's +often the same mechanisms that help understand why something's not +working as expected. In particular, looking at the generated HILTI & +C++ code often shows quickly what's going on. + +That section describes only runtime debugging output. The Spicy +toolchain also has a set of compile-time debug output streams that +shine light on various parts of the compiler's operation. To activate +that output, both ``spicyc`` and ``spicy-driver`` (and ``hiltic`` as +well) take a ``-D`` option accepting a comma-separated list of stream +tags. The following choices are available: + +``ast-dump-iterations`` + The compiler internally rewrites ASTs in multiple rounds until + they stabilize. Activating this stream will print the ASTs into + files ``dbg.*`` on disk after each round. This is pretty noisy, + and is be most helpful as a last resort if it's hard to understand + some aspect of AST processing without seeing really *all* the + changes. + +``ast-final`` + Prints out all the final ASTs, with all transformations, ID & + operator resolving, etc fully applied (and just *before* final + validation). + +``ast-orig`` + Prints out all the original ASTs, before any changes are + applied. + +``ast-pre-transformed`` + Prints out ASTs just before the AST transformation passes kick in. + Note that "transformation" here refers to a specific pass in the + pipeline that's primarily used for Spicy-to-HILTI AST rewriting. + +``ast-resolved`` + Prints out ASTs just after the pass that resolves IDs and operators has + concluded. Note that this happens once per round, with + progressively more nodes being resolved. + +``ast-scopes`` + Prints out ASTs just after scopes have been built for all nodes, + with the output including the scopes. Note that this happens + once per round, with progressively more nodes being resolved. + +``ast-transformed`` + Prints out ASTs just after the AST transformation passes kick in. + Note that "transformation" here refers to a specific pass in the + pipeline that's primarily used for Spicy-to-HILTI AST rewriting. + +``compiler`` + Prints out a various progress updates about the compiler's + internal workings. Note that ``driver`` is often a better + high-level starting point. + +``driver`` + Prints out the main high-level steps while going from source code + to final compiler output. This stream provides a good high-level + overview what's going on, with others going into more detail on + specific parts. + +``grammar`` + Prints out the parsing grammars that Spicy's parser generator + creates before code generation. + +``jit`` + Prints out details about the JIT process. + +``parser`` + Prints out details about flex/bison processing. + +``resolver`` + Prints out a detailed record of how, and why, IDs and operators + are resolved (or not) during AST rewriting. diff --git a/doc/development/index.rst b/doc/development/index.rst new file mode 100644 index 000000000..f05e10068 --- /dev/null +++ b/doc/development/index.rst @@ -0,0 +1,14 @@ + +.. _development: + +================== +Developer's Manual +================== + +.. toctree:: + :maxdepth: 2 + + architecture + testing + debugging + coding-style diff --git a/doc/development/testing.rst b/doc/development/testing.rst new file mode 100644 index 000000000..d1f8a58da --- /dev/null +++ b/doc/development/testing.rst @@ -0,0 +1,141 @@ + +.. _testing: + +Testing +======= + +Spicy's testing & CI setup includes several pieces that we discuss in +the following. + +TLDR; If you make changes, make sure that ``make check`` runs through. +You need the right ``clang-format`` (see :ref:`clang_format`) and +``clang-tidy`` (see :ref:`clang_tidy`) versions for that (from Clang +>=10). If you don't have them (or want to save time), run at least +``make test``. If that still takes too long for you, run ``make +test-core``. + +BTest +----- + +Most tests are end-to-end tests that work from Spicy (or HILTI) source +code and check that everything compiles and produces the expected +output. We use `BTest `_ to drive +these, very similar to Zeek. ``make test`` from the top-level +directory will execute these tests. You get the same effect by +changing into ``tests/`` and running ``btest -j`` there (``-j`` +parallelizes test execution). + +The most important BTest options are: + + * ``-d`` prints debugging output for failing tests to the console + + * ``-f diag.log`` records the same debugging output into ``diag.log`` + + * ``-u`` updates baselines when output changes in expected ways + (don't forget to commit the updates) + +There are some alternatives to running just all tests, per the +following: + +.. rubric:: Running a smaller subset of tests for quick results + +``btest -g spicy-core`` (or ``make test-core`` from the top-level +directory) will run a smaller set of selected tests that exercise +Spicy's main pieces. This is not sufficient to see if everything +works, but can quickly determine that something clearly doesn't. :) + +.. rubric: Running only tests that don't need JIT + +``btest -g no-jit`` runs only tests tagged as not depending on +having JIT support compiled into Spicy. + +.. rubric:: Running tests using installation after ``make install`` + +By default, btests are running completely out of the source & build +directories. If you run ``btest -a installation``, BTest will instead +switch to pulling everything from their installation locations. If you +have already deleted the build directory, you also need to have the +environment variable ``SPICY_INSTALLATION_DIRECTORY`` point to your +installation prefix, as otherwise BTest has no way of knowing where to +find Spicy. + +Unit tests +---------- + +There's a growing set of units test. These are +using ``doctest`` and are executed through ``btest`` as well, so just +running tests per above will have these included. + +Alternatively, the test binaries in the build directory can be executed to +excercise the tests, or one can use the ``check`` build target to execute all +unit tests. + +Sanitizers +---------- + +To build tools and libraries with support for Clang's address/leak +sanitizer, configure with ``--enable-sanitizer``. If Clang's ``asan`` +libraries aren't in a standard runtime library path, you'll also need +to set ``LD_LIBRARY_PATH`` (Linux) or ``DYLD_LIBRARY_PATH`` (macOS) to +point there (e.g., ``LD_LIBRARY_PATH=/opt/clang9/lib/clang/9.0.1/lib/linux``). + +When using the Spicy plugin for Zeek and Zeek hasn't been compiled +with sanitizer support, you'll also need to set ``LD_PRELOAD`` (Linux) +or ``DYLD_INSERT_LIBRARIES`` (macOS) to the shared ``asan`` library to +use (e.g., +``LD_PRELOAD=/data/clang9/lib/clang/9.0.1/lib/linux/libclang_rt.asan-x86_64.so``). +Because you probably don't want to set that permanently, the test +suite pays attention to a variable ``ZEEK_LD_PRELOAD``: If you set +that before running ``btest`` to the path you want in ``LD_PRELOAD``, +the relevant tests will copy the value for running Zeek. + +To make the sanitizer symbolize its output you need to set the +``ASAN_SYMBOLIZER_PATH`` environment variable to point to the +``llvm-symbolizer`` binary, or make sure ``llvm-symbolizer`` is in +your ``PATH``. + +.. note:: + + As we are running one of the CI build with sanitizers, it's ok not + to run this locally on a regular basis during development. + +.. todo:: + + The macOS instructions above haven't been tested, nor does + ``ZEEK_LD_PRELOAD`` lead to ``DYLD_INSERT_LIBRARIES`` being set. + +Code Quality +------------ + +Our CI runs the :ref:`clang_format` and :ref:`clang_tidy` checks, and +will fail if any of that doesn't pass. To execute these locally, run +the make target ``format`` and ``tidy``, respectively. Don't forget to +set ``CLANG_FORMAT`` and ``CLANG_TIDY`` to the right version of the +binary if they aren't in your ``PATH``. + + +CI also runs `pre-commit `_ with a +configuration pre-configured in `.pre-commit-config.yaml`. To run that +locally on every commit, install pre-commit and then put its git hook +in place through executing ``pre-commit install``; see the +`installation instructions `_ for +more details. + +Docker Builds +------------- + +We are shipping a number of Docker files in ``docker/``; see +:ref:`docker` for more information. As part of our CI, we make sure +these build OK and pass ``btest -a installation``. If you have Docker +available, you can run these individually yourself through ``make +test-`` in ``docker/``. However, usually it's fine to leave +this to CI. + + +How Test Your Branch +-------------------- + +If you run ``make check`` in the top-level directory you get the +combination of all the btests, formatting, and linting. That's the +best check to do to make sure your branch is in good shape, in +particular before filing a pull request. diff --git a/doc/examples/_usage-spicy-build.output b/doc/examples/_usage-spicy-build.output new file mode 100644 index 000000000..4c17710c0 --- /dev/null +++ b/doc/examples/_usage-spicy-build.output @@ -0,0 +1,11 @@ +# Automatically generated; do not edit. -- spicy-build -h//False +spicy-build [options] + + -d Build a debug version. + -o Destination name for the compiled executable; default is "a.out". + -s Compile the "spicy-driver" host application into executable. + -v Verbose output, display command lines executing. + -t Do not delete tmp files (useful for inspecting, and use with debugger) + +Input files may be anything can spicyc can compile to C++. + diff --git a/doc/examples/_usage-spicy-config.output b/doc/examples/_usage-spicy-config.output new file mode 100644 index 000000000..5ac9d7f22 --- /dev/null +++ b/doc/examples/_usage-spicy-config.output @@ -0,0 +1,25 @@ +# Automatically generated; do not edit. -- spicy-config -h//False + +Usage: spicy-config [options] + +Available options: + + --build Prints "debug" or "release", depending on the build configuration. + --bindir Prints the path to the directory where binaries are installed. + --cxx Print the path to the C++ compiler used to build Spicy + --cxxflags Print flags for C++ compiler. (These are addition to any that HILTI needs.) + --debug Output flags for working with debugging versions. + --distbase Print path of the Spicy source distribution. + --help Print this usage summary + --jit-compiler Prints the version of the JIT compiler if compiled with corresponding support. + --jit-support Prints 'yes' if compiled with JIT support, 'no' otherwise. + --ldflags Print flags for linker. (These are addition to any that HILTI needs.) + --libdirs Print standard Spicy library directories. + --prefix Print path of installation (TODO: same as --distbase currently) + --spicy-build Print the path to the spicy-build script. + --spicyc Print the path to the spicyc binary. + --zeek Print the path to the Zeek executable + --zeek-prefix Print the path to the Zeek installation prefix + --zeek-plugin-path Print the path to go into ZEEK_PLUGIN_PATH for enabling the Zeek Spicy plugin + --version Print Spicy version. + diff --git a/doc/examples/_usage-spicy-driver.output b/doc/examples/_usage-spicy-driver.output new file mode 100644 index 000000000..0ad19febd --- /dev/null +++ b/doc/examples/_usage-spicy-driver.output @@ -0,0 +1,25 @@ +# Automatically generated; do not edit. -- spicy-driver -h//False +Usage: cat | spicy-driver [options] ... + +Options: + + -A | --abort-on-exceptions When executing compiled code, abort() instead of throwing HILTI exceptions. + -B | --show-backtraces Include backtraces when reporting unhandled exceptions. + -D | --compiler-debug Activate compile-time debugging output for given debug streams (comma-separated; 'help' for list). + -L | --library-path Add path to list of directories to search when importing modules. + -O | --optimize Build optimized release version of generated code. + -d | --debug Include debug instrumentation into generated code. + -i | --increment Feed data incrementenally in chunks of size n. + -l | --list-parsers List available parsers and exit. + -p | --parser Use parser to process input. Only neeeded if more than one parser is available. + -R | --report-times Report a break-down of compiler's execution time. + -S | --skip-dependencies Do not automatically compile dependencies during JIT. + -v | --version Print version information. + -X | --debug-addl Implies -d and adds selected additional instrumentation (comma-separated; see 'help' for list). + +Environment variables: + + SPICY_PATH Colon-separated list of directories to search for modules. In contrast to --library-paths this flag overwrites builtin paths. + +Inputs can be .hlt, .spicy, .cc/.cxx, *.o. + diff --git a/doc/examples/_usage-spicyc.output b/doc/examples/_usage-spicyc.output new file mode 100644 index 000000000..556921902 --- /dev/null +++ b/doc/examples/_usage-spicyc.output @@ -0,0 +1,30 @@ +# Automatically generated; do not edit. -- spicyc -h//False +Usage: spicyc [options] + +Options controlling code generation: + + -A | --abort-on-exceptions When executing compiled code, abort() instead of throwing HILTI exceptions. + -B | --show-backtraces Include backtraces when reporting unhandled exceptions. + -C | --dump-code Dump all generated code to disk for debugging. + -D | --compiler-debug Activate compile-time debugging output for given debug streams (comma-separated; 'help' for list). + -e | --output-all-dependencies Output list of dependencies for all compiled modules. + -E | --output-code-dependencies Output list of dependencies for all compiled modules that require separate compilation of their own. + -K | --include-linker With --output-c++, include HILTI linker glue code. + -L | --library-path Add path to list of directories to search when importing modules. + -O | --optimize Build optimized release version of generated code. + -P | --output-prototypes Output C++ header with prototypes for public functionality. + -V | --skip-validation Don't validate ASTs (for debugging only). + -c | --output-c++ Print out all generated C++ code (including linker glue by default). + -d | --debug Include debug instrumentation into generated code. + -j | --jit-code Fully compile all code, and then execute it unless --output-to gives a file to store it + -l | --output-linker Print out only generated HILTI linker glue code. + -o | --output-to Path for saving output. + -p | --output-hilti Just output parsed HILTI code again. + -R | --report-times Report a break-down of compiler's execution time. + -S | --skip-dependencies Do not automatically compile dependencies during JIT. + -T | --keep-tmps Do not delete any temporary files created. + -v | --version Print version information. + -X | --debug-addl Implies -d and adds selected additional instrumentation (comma-separated; see 'help' for list). + +Inputs can be .hlt, .spicy, .cc/.cxx, *.hlto. + diff --git a/doc/examples/frontpage.spicy b/doc/examples/frontpage.spicy new file mode 100644 index 000000000..e979867c6 --- /dev/null +++ b/doc/examples/frontpage.spicy @@ -0,0 +1,26 @@ +# cat http-request.spicy + +module HTTP; + +const Token = /[^ \t\r\n]+/; +const WhiteSpace = /[ \t]+/; +const NewLine = /\r?\n/; + +public type RequestLine = unit { + method: Token; + : WhiteSpace; + uri: Token; + : WhiteSpace; + version: Version; + : NewLine; + + on %done { print self; } +}; + +type Version = unit { + : /HTTP\//; + number: /[0-9]+\.[0-9]+/; +}; + +# echo "GET /index.html HTTP/1.0" | spicy-driver http-request.spicy +[$method=b"GET", $uri=b"/index.html", $version=[$number=b"1.0"]] diff --git a/doc/examples/hello.spicy b/doc/examples/hello.spicy new file mode 120000 index 000000000..8876be93f --- /dev/null +++ b/doc/examples/hello.spicy @@ -0,0 +1 @@ +../../tests/spicy/doc/hello.spicy \ No newline at end of file diff --git a/doc/examples/my-http.cc b/doc/examples/my-http.cc new file mode 100644 index 000000000..b10b5cd36 --- /dev/null +++ b/doc/examples/my-http.cc @@ -0,0 +1,26 @@ +#include + +#include +#include + +using spicy::rt::fmt; + +int main(int argc, char** argv) { + hilti::rt::init(); + spicy::rt::init(); + + spicy::rt::Driver driver; + auto parser = driver.lookupParser("MyHTTP::RequestLine"); + assert(parser); + + try { + std::ifstream in("/dev/stdin", std::ios::in); + driver.processInput(**parser, in); + } catch ( const std::exception& e ) { + std::cerr << e.what() << std::endl; + } + + spicy::rt::done(); + hilti::rt::done(); + return 0; +} diff --git a/doc/examples/my-http.evt b/doc/examples/my-http.evt new file mode 100644 index 000000000..a326e4200 --- /dev/null +++ b/doc/examples/my-http.evt @@ -0,0 +1,5 @@ +protocol analyzer spicy::MyHTTP over TCP: + parse originator with MyHTTP::RequestLine, + port 12345/tcp; + +on MyHTTP::RequestLine -> event MyHTTP::request_line($conn, self.method, self.uri, self.version.number); diff --git a/doc/examples/my-http.spicy b/doc/examples/my-http.spicy new file mode 120000 index 000000000..21df9bcf4 --- /dev/null +++ b/doc/examples/my-http.spicy @@ -0,0 +1 @@ +../../tests/spicy/doc/my-http.spicy \ No newline at end of file diff --git a/doc/examples/my-http.zeek b/doc/examples/my-http.zeek new file mode 100644 index 000000000..d140400ab --- /dev/null +++ b/doc/examples/my-http.zeek @@ -0,0 +1,4 @@ +event MyHTTP::request_line(c: connection, method: string, uri: string, version: string) + { + print fmt("Zeek saw from %s: %s %s %s", c$id$orig_h, method, uri, version); + } diff --git a/doc/examples/request-line.pcap b/doc/examples/request-line.pcap new file mode 100644 index 000000000..75a294358 Binary files /dev/null and b/doc/examples/request-line.pcap differ diff --git a/doc/faq.rst b/doc/faq.rst new file mode 100644 index 000000000..2759a4ceb --- /dev/null +++ b/doc/faq.rst @@ -0,0 +1,8 @@ + +.. _faq: + +========================== +Frequently Asked Questions +========================== + +Nothing has been asked frequently, yet. diff --git a/doc/getting-started.rst b/doc/getting-started.rst new file mode 100644 index 000000000..617dfb7b1 --- /dev/null +++ b/doc/getting-started.rst @@ -0,0 +1,359 @@ + +.. _getting_started: + +Getting Started +=============== + +The following gives a short overview how to write and use Spicy +parsers. We won't use many of Spicy's features yet, but we we'll walk +through some basic code examples and demonstrate typical usage of the +Spicy toolchain. + +Hello, World! +------------- + +Here's a simple "Hello, world!" in Spicy: + +.. literalinclude:: examples/hello.spicy + :lines: 4- + +Assuming that's stored in ``hello.spicy`` and you built Spicy with JIT +support, you can compile and execute the code with Spicy's standalone +compiler ``spicyc``:: + + # spicyc -j hello.spicy + Hello, world! + +``spicyc -j`` compiles the source code into native code on the fly, +and then directly executes the result. If you run ``spicyc -c +hello.spicy``, you will see the C++ code that Spicy generates behind +the scenes. + +You can also precompile the code into an object file, and then load +that for immediate execution:: + + # spicyc -j -o hello.hlto hello.spicy + # spicyc -j hello.hlto + Hello, world! + +To compile Spicy code into an actual executable on disk, use +``spicy-build``:: + + # spicy-build -o a.out hello.spicy + # ./a.out + Hello, world! + +``spicy-build`` is a small shell script that wraps ``spicyc -c`` and +runs the resulting code through the system's C++ compiler to produce +an executable. + +.. note:: + + .. image:: _static/hilti-logo.png + :align: left + :width: 50 + + Internally, Spicy employ another intermediary languge + called *HILTI* that sits between the Spicy source code and the + generated C++ output. For more complex Spicy grammars, the HILTI + code is often easier to comprehend than the final C++ code, in + particular once we do some actual parsing. To see that + intermediary HILTI code, execute ``spicy -p hello.spicy``. HILTI + is also where the ``*.hlto`` extensions comes from: It's an + HILTI-generated object file. + +A Simple Parser +--------------- + +To actually parse some data, we now look at small a example +dissecting a HTTP-style request line, such as: ``GET /index.html +HTTP/1.0``. + +Generally, in Spicy you define parsers through types called "units" +that describe the syntax of a protocol. A set of units forms a +*grammar*. In practice, Spicy units typically correspond pretty +directly to protocol data units (PDUs) as protocol specifications tend +to define them. In addition to syntax, a Spicy unit type can also +specify semantic actions---which we call *hooks*---that will execute +during parsing as the corresponding pieces are extracted. + +Here's a simple example for parsing an HTTP request line: + +.. literalinclude:: examples/my-http.spicy + :lines: 4- + :caption: my-http.spicy + +In this example, you can see a number of things that are typical for +Spicy code: + + * A Spicy input script must always start with a ``module`` + statement defining a namespace for the script's content. + + * The layout of a piece of data is defined by creating a ``unit`` + type. The type lists individual *fields* in the order they are + to be parsed. In the example, there are two such units defined: + ``RequestLine`` and ``Version``. + + * Each field inside a unit has a type and an optional name. The + type defines how that field will be parsed from raw input data. + In the example, all fields use regular expressions instead of + actual data types (``uint32`` would be an actual type), which + means that the generated parser will match these expressions + against the input stream. Assuming a match, the corresponding + value will then be recorded with type ``bytes``, which is + Spicy's type for binary data. Note how the regular expressions + can either be given directly as a field's type (as in + ``Version``), or indirectly via globally defined constants (as + in ``RequestLine``). + + * If a field has a name, it can later be referred to for accessing + its value. Consequently, in this example all fields with + semantic meanings come with names, while those which are + unlikely to be relevant later do not (e.g., whitespace). + + * A unit field can have another unit as its type; here that's the + case for the ``version`` field in ``RequestLine``; we say that + ``Version`` is a *subunit* of ``RequestLine``. The meaning for + parsing is straight-forward: When parsing the top-level unit + reaches the field with the subunit, it switches to processing + that field according to the subunit's definition. Once the + subunit is fully parsed, the top-level unit's next field is + processed as normal from the remaining input data. + + * We can specify code to be executed when a unit has been + completely parsed by implementing a hook called ``%done``. + Inside the hook's code body, statements can refer to the unit + instance currently being parsed through an implicitly defined + ``self`` identifier. Through ``self`, they can then access any + fields already parsed by using a standard attribute notation + (``self.``). As the access to ``version`` shows, this + also works for getting to fields nested inside subunits. In the + example, we tell the generated parser to print out three of the + parsed fields whenever a ``RequestLine`` has been fully parsed. + + * The ``public`` keyword declares the parser being generated for a + unit to be exposed to external host applications wanting to + deploy it. Only exported units can later be used as the starting + point for feeding input; non-public subunits cannot be directly + instantiated from host applications. + +Now let us see how we turn this into an actual parser that we can run. +Spicy comes with a tool called ``spicy-driver`` that acts as a +generic, standalone host application for Spicy parsers: It compiles +Spicy scripts into code and then feeds them its standard input as data +to parse. Internally, ``spicy-driver`` uses much of the same machinery +as ``spicyc``, it just provides additional code kicking off the actual +parsing as well. + +With the above Spicy script in a file ``my-http.spicy``, we can use +``spicy-driver`` on it like this:: + + # echo "GET /index.html HTTP/1.0" | spicy-driver my-http.spicy + GET, /index.html, 1.0 + +As you see, the ``print`` statement inside the ``%done`` hook wrote +out the three fields as we would expect (``print`` automatically +separates its arguments with commas). If we pass something into the +driver that's malformed according to our grammar, the parser will +complain:: + + # echo "GET XXX/1.0" | spicy-driver request.spicy + [fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: failed to match regular expression (my-http.spicy:7) + +Using ``spicy-driver`` like this relies on Spicy's support for +just-in-time compilation, just like ``spicyc -j``. In the background, +there's C++ code being compiled without that we see it. Just like in +the earlier example, we can also either use ``spicyc`` to precompile +the C++ code into an object file that ``spicy-driver`` can then load, +or use ``spicy-build`` to give us an actual executable:: + + # spicyc -j -o my-http.hlto my-http.spicy + # echo "GET /index.html HTTP/1.0" | spicy-driver my-http.hlto + GET, /index.html, 1.0 + +:: + + # spicy-build -o a.out my-http.spicy + # echo "GET /index.html HTTP/1.0" | ./a.out -p MyHTTP::RequestLine + GET, /index.html, 1.0 + +If you want to see the actual parsing code that Spicy generates, use +``spicyc`` again: ``spicyc -c my-http.spicy`` will show the C++ code, +and ``spicyc -p my-http.spicy`` will show the intermediary HILTI code. + +Zeek Integration +---------------- + +Now let's use our ``RequestLine`` parser with Zeek. For that we first +need to prepare some input, and get Zeek to load the required Spicy +plugin. Then we can use the grammar that we already got to add a new +protocol analyzer to Zeek. + +.. rubric:: Preparations + +Because Zeek works from network packets, we first need a packet trace +with the payload we want to parse. We can't just use a normal HTTP +session as our simple parser wouldn't go further than just the first +line of the protocol exchange and then bail out with an error. So +instead, for our example we create a custom packet trace with a TCP +connection that carries just a single HTTP request line as its +payload:: + + # tcpdump -i lo0 -w request-line.pcap port 12345 & + # nc -l 12345 & + # echo "GET /index.html HTTP/1.0" | nc localhost 12345 + # killall tcpdump nc + +This gets us :download:`this trace file `. + +Next, we need to tell Zeek to load a Spicy plugin. If your Spicy build +has found Zeek during its ``configure`` run, it will have already +compiled and installed the plugin into Zeek's system-wide plugin +directory. You can confirm that with ``zeek -N``:: + + # zeek -N + <...> + Zeek::Spicy - Support for Spicy parsers (*.spicy, *.evt) (dynamic, version 0.3.0) + +As you can see, Zeek now reports the Spicy plugin as available among +all the other plugins that it has already built-in. + +If you don't see the Spicy plugin in there, the installation might not +have had permission to write into the Zeek plugin directory. See +:ref:`zeek_installation` for how to point Zeek to the right location +manually. + +.. rubric:: Adding a Protocol Analyzer + +Now we can go ahead and add a new protocol analyzer to Zeek. We +already got the Spicy grammar to parse our connection's payload, it's +in ``my-http.spicy``. In order to use this with Zeek, we have two +additional things to do: (1) We need to let Zeek know about our new +protocol analyzer, including when to use it; and (2) we need to define +at least one Zeek event that we want our parser to generate, so that +we can then write a Zeek script working with the information that it +extracts. + +We do both of these by creating an additional control file for Zeek: + +.. literalinclude:: examples/my-http.evt + :caption: my-http.evt + :linenos: + +The first block (lines 1-3) tells Zeek that we have a new protocol +analyzer to provide. The analyzer's Zeek-side name is +``spicy::MyHTTP``, and it's meant to run on top of TCP connections +(line 1). Lines 2-3 then provide Zeek with more specifics: The entry +point for originator-side payload is the ``MyHTTP::RequestLine`` unit +type that our Spicy grammar defines (line 2); and we want Zeek to +activate our analyzer for all connections with a responder port of +12345 (which, of course, matches the packet trace we created). + +The second block (line 5) tells the Spicy plugin that we want to +define one event. On the left-hand side of that line we give the unit +that is to trigger the event. The right-hand side defines its name and +arguments. What we are saying here is that every time a ``RequestLine`` +line has been fully parsed, we'd like a ``MyHTTP::request_line`` event +to go to Zeek. Each event instance will come with four parameters: +Three of them are the values of corresponding unit fields, accessed +just through normal Spicy expressions (inside an event argument +expression, ``self`` refers to the unit instance that has led to the +generation of the current event). The first parameter, ``$conn``, is a +"magic" keyword that lets the Spicy plugin pass the Zeek-side +connection ID (``conn_id``) to the event. + +Now we got everything in place that we need for our new protocol +analyzer---except for a Zeek script actually doing something with the +information we are parsing. Let's use this: + +.. literalinclude:: examples/my-http.zeek + :caption: my-http.zeek + +You see an Zeek event handler for the event that we just defined, +having the expected signature of four parameters matching the types of +the parameter expressions that the ``*.evt`` file specifies. The +handler's body then just prints out what it gets. + +.. _example_zeek_my_http: + +Finally we can put together our pieces by pointing Zeek to all the +files we got:: + + # zeek -Cr request-line.pcap my-http.spicy my-http.evt my-http.zeek + GET, /index.html, 1.0 + Zeek saw from 127.0.0.1: GET /index.html 1.0 + +When Zeek starts up here, it passes any ``*.spicy`` and ``*.evt`` on +to the Spicy plugin, which then first kicks off all of its code +generation. Afterwards the plugin registers the new analyzer with the +Zeek event engine. Zeek then begins processing the packet trace as +usual, now activating our new analyzer whenever it sees a TCP +connection on port 12345. Accordingly, the ``MyHTTP::request_line`` +event gets generated once the parser gets to process the session's +payload. The Zeek event handler then executes and prints the output we +would expect. (Note how we are in fact getting *two* lines of output: +The first line is still from the Spicy-side ``print`` statement inside +the ``RequestLine`` unit. One would normally remove that statement at +this point.) + +If you tried the above, you will have noticed that Zeek took a little +while to start up. That's of course because we're compiling C++ code +in the background again before any packet processing can even begin. +To accelerate the startup, we can once more precompile our analyzer +similar to what we did before with ``spicyc``. We'll use a different +tool for that here, though: ``spicyz`` is a small shell wrapper around +Zeek itself that activates a dedicated precompilation mode for the +Spicy plugin. We give ``spicyz`` (1) an output ``*.hlto`` file to +write the compiled analyzer into; and (2) the ``*.spicy`` and +``*.evt`` inputs that we handed to Zeek above:: + + # spicyz -o my-http-analyzer.hlto my-http.spicy my-http.evt + # zeek -Cr request-line.pcap my-http-analyzer.hlto my-http.zeek + GET, /index.html, 1.0 + Zeek saw from 127.0.0.1: GET /index.html 1.0 + +That ``zeek`` execution is now happening instantaneously. + +Custom Host Application +----------------------- + +Spicy parsers expose a C++ API that any application can leverage to +send them data for processing. The specifics off how to approach this +depend quite a bit on the particular needs of the application (Is it +just a single, static parser that's needed; or a set not known +upfront, and compiled dynamically? Just a single input stream, or +many? All data in one buffer, or coming in incrementally? How does the +application want to access the parsed information?). That said, the +most basic use case is quite straight-forward: feeding data into a +specific parser. Here's a small C++ program that parses input with our +``RequestLine`` parser: + +.. literalinclude:: examples/my-http.cc + :caption: my-http.cc + +.. code:: + + # spicy-build -o a.out my-http.cc my-http.spicy + # echo "GET /index.html HTTP/1.0" | ./a.out + GET, /index.html, 1.0 + # echo 'Hello, World!' | ./a.out + parse error: failed to match regular expression (my-http.spicy:7) + +The code in ``my-http.cc`` is indeed the core of what ``spicy-driver`` +does, if we ignore the dynamic JIT compilation. + +.. note:: + + Some additional pointers for application developers: + + - ``my-http.cc`` is a very stripped down version of a file that's + installed along with Spicy into + ``share/spicy/spicy-driver-host.cc``. That code is compiled in + when executing ``spicy-build`` with the ``-s`` option, as we did + above. If you look at ``spicy-driver-host.cc``, among other + things you'll also see how to dynamically query the runtime + system for the parsers available. + + - The code for the main ``spicy-driver`` tool is quite similar as + well, and in addition shows how to dynamically compiler Spicy + parsers just-in-time. diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 000000000..19c2d722b --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,95 @@ + +|today|. + +====================================================== +Spicy --- A C++ Parser Generator for Protocols & Files +====================================================== + +.. literalinclude:: examples/frontpage.spicy + :language: text + +Overview + Spicy is a C++ parser generator that makes it easy to create + robust parsers for network protocols, file formats, and more. + Spicy is a bit like a "yacc for protocols", but it's much more + than that: It's an all-in-one system enabling developers to write + attributed grammars that define both syntax and semantics of an + input format using a single, unified language. Think of Spicy as a + domain-specific scripting language for all your parsing needs. + + The Spicy toolchain turns such grammars into efficient C++ parsing + code that exposes an API to host applications for instantiating + parsers, feeding them input, and retrieving their results. At + runtime, parsing proceeds fully incrementally—and potentially + highly concurrently—on input streams of arbitrary size. + Compilation of Spicy parsers takes place either just-in-time at + startup (through Clang/LLVM), or ahead-of-time either by creating + pre-compiled shared libraries or simply by giving you C++ code that + you can link into your application. + + Spicy comes with a `Zeek `_ plugin that + enables adding new protocols to Zeek without having to write any + C++ code. You define the grammar, specify which Zeek events to + generate, and Spicy takes care of the rest. + +License + Spicy is open source and released under a BSD license, which + allows for pretty much unrestricted use as long as you leave the + license header in place. You fully own any parsers that Spicy + generates from your grammars. + +History + Spicy was originally developed as a research prototype at the + `International Computer Science Institute + `_ with funding from the `U.S. + National Science Foundation `_. Since then, + Spicy has been rebuilt from the ground up by `Corelight + `_, who has contributed the new + implementation to the Zeek Project. + +.. note:: + + Spicy is currently in a very early beta phase, it's *not* yet + ready for production usage. You'll find plenty rough edges still, + including unstable code, missing features, and confusing error + messages if you do something unexpected. Specifics of the language + and the toolset may still change as well---there's no release yet, + just a git ``master`` branch that keeps moving. We don't recommend + Spicy and its parsers for anything critical yet, but we're very + interested in feedback as we're working to stabilize all this. + +Getting in Touch +---------------- + +Having trouble using Spicy? Have ideas how to make Spicy better? We'd +like to hear from you! + + - Check out the `FAQ `_ to see if any of that helps. + - Report issues on `GitHub `_. + - Ask the ``#spicy`` channel on `Zeek's Slack `_ + - Send mail to the `Spicy mailing list `_. + * We also have a separate read-only `mailing list for following git + activity `_ + +Documentation +------------- + +.. toctree:: + :maxdepth: 2 + :numbered: + + installation + getting-started + faq + tutorial/index + programming/index + toolchain + zeek + release-notes + development/index + +Index + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 000000000..159f34dbc --- /dev/null +++ b/doc/installation.rst @@ -0,0 +1,279 @@ + +.. _installation: + +Installation +============= + +Prerequisites +------------- + +Spicy currently supports the following platforms: + + - Linux (x86_64) + + - MacOS 10.15 / Catalina + +Other platforms are unlikely to work at the moment. + +.. note:: Earlier versions of macOS aren't easily supported because of + their older C++ standard libraries. + +To build Spicy, you will need: + + - For compiling the toolchain: + + * A C++ compiler that supports C++17 (known to work are Clang 9 and GCC 9) + * `CMake `_ >= 3.13 + * `Bison `_ >= 3.4 + * `Flex `_ >= 2.6 + * `Python `_ >= 3.4 + * `Zlib `_ (no particular version) + + - For supporting just-in-time compilation (recommended): + + * `Clang/LLVM 9 `_, + with all the libraries + + .. note:: On macOS, Apple's Clang alone is not sufficient. + You can compile Spicy with that, but you won't get JIT as + it's missing the development libraries. + + - For integration with Zeek (which, in turn, requires JIT): + + * `Zeek `_ >= 3.0 + + - For testing: + + * `BTest `_ >= 0.61 (``pip install btest``) + * Bash (for BTest) + + - For building the documentation: + + * `Sphinx `_ >= 1.8 + * `Read the Docs Sphinx Theme `_ (``pip install sphinx_rtd_theme``) + +In the following we record how to get these dependencies in place on +some popular platforms. Please :issue:`file an issue <>` if you have +instructions for platforms not yet listed here. Additionally, we provide +Docker files for building on selected Linux distributions, see :ref:`docker`. + +.. note:: + + You *can* build Spicy without support for just-in-time + compilation, which will avoid the dependency on Clang/LLVM as long + as your compiler is otherwise recent enough. However, you will + then miss out on functionality and convenience. In particular, the + Zeek plugin currently requires JIT (:issue:`72`). + +.. rubric:: macOS + +Make sure you have Xcode installed, including its command tools: +``xcode-select --install``. + +If you are using `MacPorts `_: + + - ``# port install flex bison clang-9.0 cmake ninja python38 py38-pip py38-sphinx py38-sphinx_rtd_theme`` + - ``# pip install btest`` + - When running Spicy's ``configure`` (see below), add two options: + + * ``--with-cxx-compiler=/opt/local/bin/clang++-mp-9.0`` + + * ``--with-cxx-system-include-dirs=/Library/Developer/CommandLineTools/usr/include/c++/v1`` + (the MacPorts' clang doesn't seem to automatically find the system C++ headers) + +If you are using `Homebrew `_: + + - ``# brew install llvm bison flex cmake ninja python@3.8 sphinx-doc`` + - ``# pip install btest sphinx_rtd_theme`` + - When running Spicy's ``configure`` (see below), add + ``--with-cxx-compiler=/usr/local/opt/llvm/bin/clang++ --with-bison=/usr/local/opt/bison --with-flex=/usr/local/opt/flex`` + +Instead of using the MacPorts/Homebrew versions of Clang, you can also +use the prebuilt `Clang/LLVM 9.0 binary package +`_ +from LLVM's `download page `_ +and untar that into, e.g., ``/opt/clang9/``, then ``configure`` Spicy +with ``--with-cxx-compiler=/opt/clang9/bin/clang++`` + +Finally, install Zeek 3.0 from source, `per the instructions +`_ + +.. rubric:: Linux + +On Ubuntu 19 (Eoan): + + - See the :repo:`Ubuntu 19 Docker file `. + +On Alpine 3.11: + + - See the :repo:`Alpine 3.11 Docker file `. + +On CentOS 8 / RedHat 8: + + - See the :repo:`CentOS 8 Docker file `. + +.. rubric:: Clang/LLVM Source Installation + +If your OS/distribution doesn't come with suitable Clang/LLVM +packages, it's not too difficult to compile that yourself:: + + # mkdir -p /opt/clang9/src + # cd /opt/clang9/src + # git clone --branch release/9.x --single-branch https://github.com/llvm/llvm-project.git + # mkdir llvm-project/build + # cd llvm-project/build + # cmake -DLLVM_ENABLE_PROJECTS="clang;compiler-rt;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/clang9 -DLLVM_TARGETS_TO_BUILD=host -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON ../llvm + # make && make install + +That will give you ``clang++`` in ``/opt/clang9/bin``, so that you can +``configure`` Spicy with +``--with-cxx-compiler=/opt/clang9/bin/clang++``. + +Installing the Spicy Toolchain +------------------------------ + +Get the code:: + + # git clone --recursive https://github.com/zeek/spicy + +The short version to install Spicy is the standard ``./configure && +make && make install``. However, you'll likely need to customize the +build a bit, so we'll walk through some of the options in the +following. + +Spicy's ``configure`` script has a couple of ways to tell the build +system about the right compiler. The easiest is to point it to +the right ``clang++`` version to use:: + + # ./configure --with-cxx-compiler=/opt/clang9/bin/clang++ + +Spicy by default installs into ``/usr/local``. You can change that by +giving ``configure`` a ``--prefix``:: + + # ./configure --prefix=/opt/spicy + +If Zeek is installed but not in its standard location (i.e., +``/usr/local/zeek``), you can tell ``configure`` the prefix where to +look for it:: + + # ./configure --with-zeek=/opt/zeek + +The final ``configure`` output will summarize your build's configuration. +To ensure that both JIT and Zeek support are enabled, verify the presence of +the following lines:: + + JIT enabled: yes + Zeek plugin enabled: yes + +.. note:: + + ``configure`` has a few more flags that may be helpful, see its + ``--help`` output. For developers, the following may be particular + useful: + + - ``--enable-debug``: compile a non-optimized debug version + - ``--enable-sanitizer``: enable Clang's address & leak sanitizers + - ``--generator=Ninja``: use the faster ``ninja`` build system instead of ``make`` + - ``--enable-ccache``: use the ``ccache`` compiler cache to speed up compilation + + Using Ninja and ``ccache`` will speed up compile times. On Linux, + compiling will also be quite a bit faster if you have the "Gold + linker" available. To check if you do, see if ``which ld.gold`` + returns anything. If yes, ``configure`` will automatically pick it + up. + +Once you have configured Spicy, running ``make`` will change into the +newly created ``build`` directory and start the compilation there. +Once finished, ``make test`` will execute the test suite. It will take +a bit, but all tests should be passing (unless explicitly reported as +expected to fail). Finally, ``make install`` will install Spicy +system-wide into the configured prefix. If you are installing into a +non-standard location, make sure that ``/bin`` is in your +``PATH``. + +.. note:: You can also use the Spicy tools directly out of the build + directory without installing it, the binaries land in ``build/bin``. + +To build Spicy's documentation, run ``make`` inside the ``docs/`` directory. +Documentation will be located in ``build/doc/html``. + +.. _docker: + +Using Docker +------------ + +The Spicy distribution comes with a :repo:`set of Docker files +` that create images for selected Linux distributions. We walk +through how to use these in the following. We also welcome +contributions to support more Linux distributions. If you create a new +Docker file, please file a :pr:`pull request <>`. + +Pre-requisites +~~~~~~~~~~~~~~ + +You first need to install Docker on your host system if you haven't yet. + +.. rubric:: Linux + +All major Linux distributions provide Docker. Install it using your +package manager. Alternatively, follow the official +`instructions `__. + +.. rubric:: macOS + +Install `Docker Desktop for Mac +`_ following the official +`instructions `__. + +.. note:: + + Docker Desktop for Mac uses a VM behind the scenes to host the + Docker runtime environment. By default it allocates 2 GB of RAM to + the VM. This is not enough to compile Spicy or Zeek and will cause + an error that looks something like this:: + + c++: internal compiler error: Killed (program cc1plus) + Please submit a full bug report, + with preprocessed source if appropriate. + See for instructions. + + This is due to the VM hitting an out-of-memory condition. To avoid + this you will need to allocate more RAM to the VM. Click on the Docker + Icon in your menubar and select "Preferences". Click on the "Advanced" + tab and then use the slider to select 8 GB of RAM. Docker Desktop will + restart and then you will be ready to go. + +Build Your Spicy Container +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can build your Spicy container from one of the Docker files coming +with Spicy: Go into Spicy's ``docker`` directory and run ``make`` to +see the container platforms available:: + + # cd spicy + # make + + Run "make build-", then "make run-". + + Available platforms: + + alpine-3.11 + ubuntu-19.10 + +To build a Spicy container image based on, for example, Ubuntu 19.10, run:: + + # make build-ubuntu-19.10 + +Once the container build has finished, you can double-check that the +container image is now available in your local Docker registry:: + + # docker images | grep -e spicy -e REPO + REPOSITORY TAG IMAGE ID CREATED SIZE + spicy-ubuntu-19.10 0.2.0-dev 6f48daf3ade3 2 minutes ago 2.45GB + spicy-ubuntu-19.10 latest 6f48daf3ade3 2 minutes ago 2.45GB + +Great, let's fire it up! :: + + # make run-ubuntu-19.10 + root@bc93113300bc:~# spicyc --version + 0.2.0-dev diff --git a/doc/programming/debugging.rst b/doc/programming/debugging.rst new file mode 100644 index 000000000..2f13ef251 --- /dev/null +++ b/doc/programming/debugging.rst @@ -0,0 +1,143 @@ + +.. _debugging: + +========= +Debugging +========= + +It can be challenging to track down the specifics of what a parser is +doing (or not doing) because often there's no directly observable +effect. However, Spicy comes with debugging support that helps during +parser development. + +Generally, getting debugging support requires running ``spicyc`` or +``spicy-driver`` with option ``-d``; that enables generating debug +versions of the generated C++ code. In addition, the option ``-X +`` may enable additional, more expensive debug instrumentation, +as discussed below. Any use of ``-X`` implicitly turns on ``-d``. + +Debug Hooks +=========== + +The simplest way to learn more about what's going on is to add hooks +with ``print`` statements to your grammar. That's rather disruptive +though, and hence there are also special ``%debug`` unit hooks which +only get compiled into the resulting code if ``spicy-driver`` is run +with debugging enabled (``-d``): + +.. spicy-code:: debug-hooks.spicy + + module Test; + + public type test = unit { + a: /1234/ %debug { + print self.a; + } + + b: /567890/; + + on b %debug { print self.b; } + }; + + +.. spicy-output:: debug-hooks.spicy 1 + :exec: printf "1234567890" | spicy-driver -d %INPUT + :show-with: debugging.spicy + +.. spicy-output:: debug-hooks.spicy 2 + :exec: printf "1234567890" | spicy-driver %INPUT + :show-with: debugging.spicy + +Debug Streams +============= + +A second form of debugging support uses runtime *debug streams* +instrumenting the generated parsers to log activity as they are +parsing their input. If you run ``spicy-driver`` with ``-d``, you can +set the environment variable ``HILTI_DEBUG`` to one or more debug +stream names to select the desired information (see below for the +list). Execution will then print debug information to standard error:: + + > echo "GET /index.html HTTP/1.0" | HILTI_DEBUG=spicy spicy-driver -d http-request.spicy + [spicy] Request::RequestLine + [spicy] method = GET + [spicy] anon_2 = + [spicy] uri = /index.html + [spicy] anon_3 = + [spicy] Request::Version + [spicy] anon = HTTP/ + [spicy] number = 1.0 + [spicy] version = [$anon=b"HTTP/", $number=b"1.0"] + [spicy] anon_4 = \n + GET, /index.html, 1.0 + +The available debug streams include: + +``spicy`` + Logs unit fields and variables as they receive values. This is + often the most helpful output as it shows rather concisely what + the parser is doing, and in particular how far it gets in cases + where it doesn't parse something correctly. + +``spicy-verbose`` + Logs various internals about the parsing process, including the + grammar rules currently being parsed, the current input, and lexer + tokens. + + This stream is primarily intended for debugging the Spicy compiler + itself, but it can be helpful for understanding in particular that + data waiting to be parsed. + +``hilti-trace`` + This is a HILTI-level debug stream that records every HILTI + instruction being executed. To use this, you must run + ``spicy-driver`` with ``-X hilti-trace``. + + This stream is primarily intended for debugging the Spicy + compiler itself. + +``hilti-flow`` + This is a HILTI-level debug level recording flow information like + function calls. To use this, you must run ``spicy-driver`` with + ``-X hilti-trace``. + + This stream is primarily intended for debugging the Spicy compiler + itself, although it may also be helpful to understand the internal + control flow when writing a grammar. + +Multiple streams can be enabled by separating them with colons. + +Exceptions +========== + +.. todo:: Explain ``-A`` and ``-B`` + + +Inspecting Generated Code +========================= + +Using ``spicyc`` you can inspect the code that's being generated for a +given Spicy grammar: + +- ``spicyc -p`` output the intermediary HILTI code. The code tends to + be pretty intuitively readable. Even if you don't know all the + specifics of HILTI, much of the code is rather close to Spicy + itself. + +- ``spicyc -c`` outputs the final C++ code. If you add ``-L``, the + output will also include additional code generated by HILTI's + linker (which enables cross-module functionality). + +- When JITing a grammar with ``spicyc -j``, running with ``-D + dump-code`` will record all generated intermediary code (HILTI code, + C++ code, LLVM bitcode) into files ``dbg.*`` inside the current + directory. + +Skipping validation +=================== + +When working on the Spicy code, it can be helpful to disable internal +validation of generated HILTI code with ``-V``. That way, one can +often still see the HILTI code even if it's malformed. Note, however, +that Spicy may end up crashing if broken HILTI code gets passed into +later stages. diff --git a/doc/programming/examples.rst b/doc/programming/examples.rst new file mode 100644 index 000000000..add399239 --- /dev/null +++ b/doc/programming/examples.rst @@ -0,0 +1,23 @@ + + +.. _examples: + +======== +Examples +======== + +We collect some example Spicy parsers here that come with the Spicy +distribution: + +:repo:`HTTP ` + A nearly complete HTTP parser. This parser was used with the + original Spicy prototype to compare output with Zeek's native + handwritten HTTP parser. We observed only negligible differences. + +:repo:`DNS ` + A comprehensive DNS parser. This parser was used with the + original Spicy prototype to compare output with Zeek's native + handwritten HTTP parser. We observed only negligible differences. + + The DNS parser is a good example of using :ref:`random access + `. diff --git a/doc/programming/examples/_anonymous-field.spicy b/doc/programming/examples/_anonymous-field.spicy new file mode 100644 index 000000000..6082f5429 --- /dev/null +++ b/doc/programming/examples/_anonymous-field.spicy @@ -0,0 +1,9 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + x: int8; + : int8 { print $$; } # anonymous field + y: int8; + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_anonymous-field.spicy.output b/doc/programming/examples/_anonymous-field.spicy.output new file mode 100644 index 000000000..7a015b243 --- /dev/null +++ b/doc/programming/examples/_anonymous-field.spicy.output @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- printf '\01\02\03' | spicy-driver %INPUT/printf '\01\02\03' | spicy-driver %INPUT/False +# printf '\01\02\03' | spicy-driver foo.spicy +2 +[$x=1, $y=3] diff --git a/doc/programming/examples/_basic-unit-module-with-default.spicy b/doc/programming/examples/_basic-unit-module-with-default.spicy new file mode 100644 index 000000000..30edac9e4 --- /dev/null +++ b/doc/programming/examples/_basic-unit-module-with-default.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type Foo = unit { + version: uint32 &default=42; +}; + +global f: Foo; +print f; +print "version is %s" % f.version; \ No newline at end of file diff --git a/doc/programming/examples/_basic-unit-module-with-default.spicy.output b/doc/programming/examples/_basic-unit-module-with-default.spicy.output new file mode 100644 index 000000000..3ce7523da --- /dev/null +++ b/doc/programming/examples/_basic-unit-module-with-default.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- spicyc -j %INPUT//False +[$version=(not set)] +version is 42 diff --git a/doc/programming/examples/_basic-unit-module.spicy b/doc/programming/examples/_basic-unit-module.spicy new file mode 100644 index 000000000..08127ec9a --- /dev/null +++ b/doc/programming/examples/_basic-unit-module.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type Foo = unit { + version: uint32; +}; + +global f: Foo; +f.version = 42; +print f; \ No newline at end of file diff --git a/doc/programming/examples/_basic-unit-module.spicy.output b/doc/programming/examples/_basic-unit-module.spicy.output new file mode 100644 index 000000000..61bbfee01 --- /dev/null +++ b/doc/programming/examples/_basic-unit-module.spicy.output @@ -0,0 +1,2 @@ +# Automatically generated; do not edit. -- spicyc -j %INPUT//False +[$version=42] diff --git a/doc/programming/examples/_basic-unit-parse-byte-order.spicy b/doc/programming/examples/_basic-unit-parse-byte-order.spicy new file mode 100644 index 000000000..c5960e645 --- /dev/null +++ b/doc/programming/examples/_basic-unit-parse-byte-order.spicy @@ -0,0 +1,12 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +import spicy; + +public type Foo = unit { + version: uint32 &byte-order=spicy::ByteOrder::Little; + + on %done { + print "0x%x" % self.version; + } +}; \ No newline at end of file diff --git a/doc/programming/examples/_basic-unit-parse-byte-order.spicy.output b/doc/programming/examples/_basic-unit-parse-byte-order.spicy.output new file mode 100644 index 000000000..fe762079c --- /dev/null +++ b/doc/programming/examples/_basic-unit-parse-byte-order.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\01\02\03\04' | spicy-driver %INPUT/printf '\01\02\03\04' | spicy-driver %INPUT/False +# printf '\01\02\03\04' | spicy-driver foo.spicy +0x4030201 diff --git a/doc/programming/examples/_basic-unit-parse.spicy b/doc/programming/examples/_basic-unit-parse.spicy new file mode 100644 index 000000000..571d10f83 --- /dev/null +++ b/doc/programming/examples/_basic-unit-parse.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + version: uint32; + + on %done { + print "0x%x" % self.version; + } +}; \ No newline at end of file diff --git a/doc/programming/examples/_basic-unit-parse.spicy.output b/doc/programming/examples/_basic-unit-parse.spicy.output new file mode 100644 index 000000000..eeac28275 --- /dev/null +++ b/doc/programming/examples/_basic-unit-parse.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\01\02\03\04' | spicy-driver %INPUT/printf '\01\02\03\04' | spicy-driver %INPUT/False +# printf '\01\02\03\04' | spicy-driver foo.spicy +0x1020304 diff --git a/doc/programming/examples/_constant-field.spicy b/doc/programming/examples/_constant-field.spicy new file mode 100644 index 000000000..f58d43bdc --- /dev/null +++ b/doc/programming/examples/_constant-field.spicy @@ -0,0 +1,7 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + bar: b"bar"; + on %done { print self.bar; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_constant-field.spicy.output_1 b/doc/programming/examples/_constant-field.spicy.output_1 new file mode 100644 index 000000000..67b61f18b --- /dev/null +++ b/doc/programming/examples/_constant-field.spicy.output_1 @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf 'bar' | spicy-driver %INPUT/printf 'bar' | spicy-driver %INPUT/False +# printf 'bar' | spicy-driver foo.spicy +bar diff --git a/doc/programming/examples/_constant-field.spicy.output_2 b/doc/programming/examples/_constant-field.spicy.output_2 new file mode 100644 index 000000000..0f0aa0996 --- /dev/null +++ b/doc/programming/examples/_constant-field.spicy.output_2 @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf 'foo' | spicy-driver %INPUT/printf 'foo' | spicy-driver %INPUT/True +# printf 'foo' | spicy-driver foo.spicy +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: expecting 'bar' (foo.spicy:5) diff --git a/doc/programming/examples/_debug-hooks.spicy b/doc/programming/examples/_debug-hooks.spicy new file mode 100644 index 000000000..2c23a99fb --- /dev/null +++ b/doc/programming/examples/_debug-hooks.spicy @@ -0,0 +1,12 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type test = unit { + a: /1234/ %debug { + print self.a; + } + + b: /567890/; + + on b %debug { print self.b; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_debug-hooks.spicy.output b/doc/programming/examples/_debug-hooks.spicy.output new file mode 100644 index 000000000..70a090bda --- /dev/null +++ b/doc/programming/examples/_debug-hooks.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf "1234567890" | spicy-driver %INPUT/printf "1234567890" | spicy-driver %INPUT/False +# printf "1234567890" | spicy-driver debugging.spicy + diff --git a/doc/programming/examples/_debug-hooks.spicy.output_1 b/doc/programming/examples/_debug-hooks.spicy.output_1 new file mode 100644 index 000000000..425580abb --- /dev/null +++ b/doc/programming/examples/_debug-hooks.spicy.output_1 @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- printf "1234567890" | spicy-driver -d %INPUT/printf "1234567890" | spicy-driver -d %INPUT/False +# printf "1234567890" | spicy-driver -d debugging.spicy +1234 +567890 diff --git a/doc/programming/examples/_debug-hooks.spicy.output_2 b/doc/programming/examples/_debug-hooks.spicy.output_2 new file mode 100644 index 000000000..70a090bda --- /dev/null +++ b/doc/programming/examples/_debug-hooks.spicy.output_2 @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf "1234567890" | spicy-driver %INPUT/printf "1234567890" | spicy-driver %INPUT/False +# printf "1234567890" | spicy-driver debugging.spicy + diff --git a/doc/programming/examples/_parse-address.spicy b/doc/programming/examples/_parse-address.spicy new file mode 100644 index 000000000..11bef6914 --- /dev/null +++ b/doc/programming/examples/_parse-address.spicy @@ -0,0 +1,9 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +import spicy; + +public type Foo = unit { + ip: addr &ipv6 &byte-order=spicy::ByteOrder::Little; + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-address.spicy.output b/doc/programming/examples/_parse-address.spicy.output new file mode 100644 index 000000000..8fac5af86 --- /dev/null +++ b/doc/programming/examples/_parse-address.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '1234567890123456' | spicy-driver %INPUT/printf '1234567890123456' | spicy-driver %INPUT/False +# printf '1234567890123456' | spicy-driver foo.spicy +[$ip=3635:3433:3231:3039:3837:3635:3433:3231] diff --git a/doc/programming/examples/_parse-bitfield-enum.spicy b/doc/programming/examples/_parse-bitfield-enum.spicy new file mode 100644 index 000000000..c05f14d1d --- /dev/null +++ b/doc/programming/examples/_parse-bitfield-enum.spicy @@ -0,0 +1,13 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +import spicy; + +type X = enum { A = 1, B = 2 }; + +public type Foo = unit { + f: bitfield(8) { + x1: 0..3 &convert=X($$); + x2: 4..7 &convert=X($$); + } { print self.f.x1, self.f.x2; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-bitfield-enum.spicy.output b/doc/programming/examples/_parse-bitfield-enum.spicy.output new file mode 100644 index 000000000..4fa6d8250 --- /dev/null +++ b/doc/programming/examples/_parse-bitfield-enum.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\41' | spicy-driver %INPUT/printf '\41' | spicy-driver %INPUT/False +# printf '\41' | spicy-driver foo.spicy +X::A, X::B diff --git a/doc/programming/examples/_parse-bitfield.spicy b/doc/programming/examples/_parse-bitfield.spicy new file mode 100644 index 000000000..56d30d00e --- /dev/null +++ b/doc/programming/examples/_parse-bitfield.spicy @@ -0,0 +1,15 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + f: bitfield(32) { + x1: 0; + x2: 1..2; + x3: 3..4; + }; + + on %done { + print self.f.x1, self.f.x2, self.f.x3; + print self; + } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-bitfield.spicy.output b/doc/programming/examples/_parse-bitfield.spicy.output new file mode 100644 index 000000000..722372327 --- /dev/null +++ b/doc/programming/examples/_parse-bitfield.spicy.output @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- printf '\01\02\03\04' | spicy-driver %INPUT/printf '\01\02\03\04' | spicy-driver %INPUT/False +# printf '\01\02\03\04' | spicy-driver foo.spicy +0, 2, 0 +[$f=(0, 2, 0)] diff --git a/doc/programming/examples/_parse-convert.spicy b/doc/programming/examples/_parse-convert.spicy new file mode 100644 index 000000000..433251f82 --- /dev/null +++ b/doc/programming/examples/_parse-convert.spicy @@ -0,0 +1,9 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +import spicy; + +public type Foo = unit { + x: bytes &eod &convert=$$.to_uint(); + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-convert.spicy.output b/doc/programming/examples/_parse-convert.spicy.output new file mode 100644 index 000000000..581ece319 --- /dev/null +++ b/doc/programming/examples/_parse-convert.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf 12345 | spicy-driver %INPUT/printf 12345 | spicy-driver %INPUT/False +# printf 12345 | spicy-driver foo.spicy +[$x=12345] diff --git a/doc/programming/examples/_parse-filter.spicy b/doc/programming/examples/_parse-filter.spicy new file mode 100644 index 000000000..1989e2c94 --- /dev/null +++ b/doc/programming/examples/_parse-filter.spicy @@ -0,0 +1,15 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type Filter = unit { + %filter + + : bytes &eod &chunked { + self.forward($$.upper()); + } +}; + +public type Foo = unit { + on %init { self.connect_filter(new Filter); } + x: bytes &size=5 { print self.x; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-filter.spicy.output b/doc/programming/examples/_parse-filter.spicy.output new file mode 100644 index 000000000..8681e45a4 --- /dev/null +++ b/doc/programming/examples/_parse-filter.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf 'aBcDe' | spicy-driver %INPUT/printf 'aBcDe' | spicy-driver %INPUT/False +# printf 'aBcDe' | spicy-driver foo.spicy +ABCDE diff --git a/doc/programming/examples/_parse-if.spicy b/doc/programming/examples/_parse-if.spicy new file mode 100644 index 000000000..76ae9e268 --- /dev/null +++ b/doc/programming/examples/_parse-if.spicy @@ -0,0 +1,11 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + a: int8; + b: int8 if ( self.a == 1 ); + c: int8 if ( self.a % 2 == 0 ); + d: int8; + + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-if.spicy.output b/doc/programming/examples/_parse-if.spicy.output new file mode 100644 index 000000000..dd492b13b --- /dev/null +++ b/doc/programming/examples/_parse-if.spicy.output @@ -0,0 +1,6 @@ +# Automatically generated; do not edit. -- printf '\01\02\03\04' | spicy-driver %INPUT; printf '\02\02\03\04' | spicy-driver %INPUT/printf '\01\02\03\04' | spicy-driver %INPUT; printf '\02\02\03\04' | spicy-driver %INPUT/False +# printf '\01\02\03\04' | spicy-driver foo.spicy; printf '\02\02\03\04' | spicy-driver foo.spicy +[$a=1, $b=2, $c=(not set), $d=3] + +# printf '\01\02\03\04' | spicy-driver foo.spicy; printf '\02\02\03\04' | spicy-driver foo.spicy +[$a=2, $b=(not set), $c=2, $d=3] diff --git a/doc/programming/examples/_parse-look-ahead.spicy b/doc/programming/examples/_parse-look-ahead.spicy new file mode 100644 index 000000000..30fa93170 --- /dev/null +++ b/doc/programming/examples/_parse-look-ahead.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + data: uint8[]; + : /EOD/; + x : int8; + + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-look-ahead.spicy.output b/doc/programming/examples/_parse-look-ahead.spicy.output new file mode 100644 index 000000000..b5a71c92f --- /dev/null +++ b/doc/programming/examples/_parse-look-ahead.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\01\02\03EOD\04' | spicy-driver %INPUT/printf '\01\02\03EOD\04' | spicy-driver %INPUT/False +# printf '\01\02\03EOD\04' | spicy-driver foo.spicy +[$data=[1, 2, 3], $x=4] diff --git a/doc/programming/examples/_parse-parse.spicy b/doc/programming/examples/_parse-parse.spicy new file mode 100644 index 000000000..aebf4a15e --- /dev/null +++ b/doc/programming/examples/_parse-parse.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + x: bytes &size=2; + y: uint16 &parse-from=self.x; + z: bytes &size=2; + + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-parse.spicy.output b/doc/programming/examples/_parse-parse.spicy.output new file mode 100644 index 000000000..a4338e26e --- /dev/null +++ b/doc/programming/examples/_parse-parse.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\x01\x02\x03\04' | spicy-driver %INPUT/printf '\x01\x02\x03\04' | spicy-driver %INPUT/False +# printf '\x01\x02\x03\04' | spicy-driver foo.spicy +[$x=b"\x01\x02", $y=258, $z=b"\x03\x04"] diff --git a/doc/programming/examples/_parse-random-access.spicy b/doc/programming/examples/_parse-random-access.spicy new file mode 100644 index 000000000..26b4bb7aa --- /dev/null +++ b/doc/programming/examples/_parse-random-access.spicy @@ -0,0 +1,23 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + %random-access + + on %init() { self.start = self.input(); } + + a: A { self.set_input(self.start); } + b: B; + + on %done() { print self; } + + var start: iterator; +}; + +type A = unit { + x: uint32; +}; + +type B = unit { + y: bytes &size=4; +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-random-access.spicy.output b/doc/programming/examples/_parse-random-access.spicy.output new file mode 100644 index 000000000..17b75a79c Binary files /dev/null and b/doc/programming/examples/_parse-random-access.spicy.output differ diff --git a/doc/programming/examples/_parse-reassembly.spicy b/doc/programming/examples/_parse-reassembly.spicy new file mode 100644 index 000000000..e6b94e32d --- /dev/null +++ b/doc/programming/examples/_parse-reassembly.spicy @@ -0,0 +1,20 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + + sink data; + + on %init { + self.data.connect(new Bar); + self.data.write(b"567", 5); + self.data.write(b"89", 8); + self.data.write(b"012", 0); + self.data.write(b"34", 3); + } +}; + +public type Bar = unit { + s: bytes &eod; + on %done { print self.s; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-reassembly.spicy.output b/doc/programming/examples/_parse-reassembly.spicy.output new file mode 100644 index 000000000..53dfae9d9 --- /dev/null +++ b/doc/programming/examples/_parse-reassembly.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- spicy-driver -p Test::Foo %INPUT printf '\13GET /a/b/c\n' | spicy-driver -p Test::A %INPUT/printf '\13GET /a/b/c\n' | spicy-driver -p Test::A %INPUT/False +# printf '\13GET /a/b/c\n' | spicy-driver -p Test::A foo.spicy +B, [$path=b"/a/b/c"] +A, [$length=11, $data=b"GET /a/b/c\n", $b=] diff --git a/doc/programming/examples/_parse-switch-lhead-2.spicy b/doc/programming/examples/_parse-switch-lhead-2.spicy new file mode 100644 index 000000000..839991bcd --- /dev/null +++ b/doc/programming/examples/_parse-switch-lhead-2.spicy @@ -0,0 +1,19 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type A = unit { + a: b"A"; +}; + +type B = unit { + b: uint16(0xffff); +}; + +public type Foo = unit { + switch { + a: A; + b: B; + }; + + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-switch-lhead-2.spicy.output b/doc/programming/examples/_parse-switch-lhead-2.spicy.output new file mode 100644 index 000000000..43f529d99 --- /dev/null +++ b/doc/programming/examples/_parse-switch-lhead-2.spicy.output @@ -0,0 +1,6 @@ +# Automatically generated; do not edit. -- printf 'A ' | spicy-driver %INPUT; printf '\377\377' | spicy-driver %INPUT/printf 'A ' | spicy-driver %INPUT; printf '\377\377' | spicy-driver %INPUT/False +# printf 'A ' | spicy-driver foo.spicy; printf '\377\377' | spicy-driver foo.spicy +[$a=[$a=b"A"], $b=(not set)] + +# printf 'A ' | spicy-driver foo.spicy; printf '\377\377' | spicy-driver foo.spicy +[$a=(not set), $b=[$b=65535]] diff --git a/doc/programming/examples/_parse-switch-lhead.spicy b/doc/programming/examples/_parse-switch-lhead.spicy new file mode 100644 index 000000000..b8101866e --- /dev/null +++ b/doc/programming/examples/_parse-switch-lhead.spicy @@ -0,0 +1,12 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + switch { + a: b"A"; + b: b"B"; + c: b"C"; + }; + + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-switch-lhead.spicy.output b/doc/programming/examples/_parse-switch-lhead.spicy.output new file mode 100644 index 000000000..41e20b846 --- /dev/null +++ b/doc/programming/examples/_parse-switch-lhead.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf 'A' | spicy-driver %INPUT/printf 'A' | spicy-driver %INPUT/False +# printf 'A' | spicy-driver foo.spicy +[$a=b"A", $b=(not set), $c=(not set)] diff --git a/doc/programming/examples/_parse-switch.spicy b/doc/programming/examples/_parse-switch.spicy new file mode 100644 index 000000000..a21a37b64 --- /dev/null +++ b/doc/programming/examples/_parse-switch.spicy @@ -0,0 +1,13 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + x: bytes &size=1; + switch ( self.x ) { + b"A" -> a8: int8; + b"B" -> a16: int16; + b"C" -> a32: int32; + }; + + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-switch.spicy.output b/doc/programming/examples/_parse-switch.spicy.output new file mode 100644 index 000000000..25b125c16 --- /dev/null +++ b/doc/programming/examples/_parse-switch.spicy.output @@ -0,0 +1,6 @@ +# Automatically generated; do not edit. -- printf 'A\01' | spicy-driver %INPUT; printf 'B\01\02' | spicy-driver %INPUT/printf 'A\01' | spicy-driver %INPUT; printf 'B\01\02' | spicy-driver %INPUT/False +# printf 'A\01' | spicy-driver foo.spicy; printf 'B\01\02' | spicy-driver foo.spicy +[$x=b"A", $a8=1, $a16=(not set), $a32=(not set)] + +# printf 'A\01' | spicy-driver foo.spicy; printf 'B\01\02' | spicy-driver foo.spicy +[$x=b"B", $a8=(not set), $a16=258, $a32=(not set)] diff --git a/doc/programming/examples/_parse-unit-params.spicy b/doc/programming/examples/_parse-unit-params.spicy new file mode 100644 index 000000000..e2eed3fa6 --- /dev/null +++ b/doc/programming/examples/_parse-unit-params.spicy @@ -0,0 +1,11 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type Bar = unit(a: string) { + x: uint8 { print "%s: %u" % (a, self.x); } +}; + +public type Foo = unit { + y: Bar("Spicy"); + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-unit-params.spicy.output b/doc/programming/examples/_parse-unit-params.spicy.output new file mode 100644 index 000000000..43f0acfd0 --- /dev/null +++ b/doc/programming/examples/_parse-unit-params.spicy.output @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- printf '\01\02' | spicy-driver %INPUT/printf '\01\02' | spicy-driver %INPUT/False +# printf '\01\02' | spicy-driver foo.spicy +"Spicy": 1 +[$y=[$x=1]] diff --git a/doc/programming/examples/_parse-vector-foreach.spicy b/doc/programming/examples/_parse-vector-foreach.spicy new file mode 100644 index 000000000..27a7dc1e2 --- /dev/null +++ b/doc/programming/examples/_parse-vector-foreach.spicy @@ -0,0 +1,6 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + x: uint8[5] foreach { print $$, self.x; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-vector-foreach.spicy.output b/doc/programming/examples/_parse-vector-foreach.spicy.output new file mode 100644 index 000000000..e4dace1fd --- /dev/null +++ b/doc/programming/examples/_parse-vector-foreach.spicy.output @@ -0,0 +1,7 @@ +# Automatically generated; do not edit. -- printf '\01\02\03\04\05' | spicy-driver %INPUT/printf '\01\02\03\04\05' | spicy-driver %INPUT/False +# printf '\01\02\03\04\05' | spicy-driver foo.spicy +1, [] +2, [1] +3, [1, 2] +4, [1, 2, 3] +5, [1, 2, 3, 4] diff --git a/doc/programming/examples/_parse-vector.spicy b/doc/programming/examples/_parse-vector.spicy new file mode 100644 index 000000000..5e1f357d7 --- /dev/null +++ b/doc/programming/examples/_parse-vector.spicy @@ -0,0 +1,7 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + x: uint8[5]; + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_parse-vector.spicy.output b/doc/programming/examples/_parse-vector.spicy.output new file mode 100644 index 000000000..445aa71af --- /dev/null +++ b/doc/programming/examples/_parse-vector.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\01\02\03\04\05' | spicy-driver %INPUT/printf '\01\02\03\04\05' | spicy-driver %INPUT/False +# printf '\01\02\03\04\05' | spicy-driver foo.spicy +[$x=[1, 2, 3, 4, 5]] diff --git a/doc/programming/examples/_regexp.spicy b/doc/programming/examples/_regexp.spicy new file mode 100644 index 000000000..1eba5ca99 --- /dev/null +++ b/doc/programming/examples/_regexp.spicy @@ -0,0 +1,7 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + x: /Foo.*Bar/; + on %done { print self; } +}; \ No newline at end of file diff --git a/doc/programming/examples/_regexp.spicy.output b/doc/programming/examples/_regexp.spicy.output new file mode 100644 index 000000000..b6f5b01c7 --- /dev/null +++ b/doc/programming/examples/_regexp.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf 'Foo12345Bar' | spicy-driver %INPUT/printf 'Foo12345Bar' | spicy-driver %INPUT/False +# printf 'Foo12345Bar' | spicy-driver foo.spicy +[$x=b"Foo12345Bar"] diff --git a/doc/programming/examples/_unit-params-vector.spicy b/doc/programming/examples/_unit-params-vector.spicy new file mode 100644 index 000000000..8893e3886 --- /dev/null +++ b/doc/programming/examples/_unit-params-vector.spicy @@ -0,0 +1,12 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type Bar = unit(mult: int8) { + x: int8 &convert=($$ * mult); + on %done { print self.x; } +}; + +public type Foo = unit { + x: int8; + y: (Bar(self.x))[]; # Element constructor must be in "(...)" +}; \ No newline at end of file diff --git a/doc/programming/examples/_unit-params-vector.spicy.output b/doc/programming/examples/_unit-params-vector.spicy.output new file mode 100644 index 000000000..2735d8e6b --- /dev/null +++ b/doc/programming/examples/_unit-params-vector.spicy.output @@ -0,0 +1,5 @@ +# Automatically generated; do not edit. -- printf '\05\01\02\03' | spicy-driver %INPUT/printf '\05\01\02\03' | spicy-driver %INPUT/False +# printf '\05\01\02\03' | spicy-driver foo.spicy +5 +10 +15 diff --git a/doc/programming/examples/_unit-params.spicy b/doc/programming/examples/_unit-params.spicy new file mode 100644 index 000000000..8469c30d8 --- /dev/null +++ b/doc/programming/examples/_unit-params.spicy @@ -0,0 +1,11 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +type Bar = unit(msg: string, mult: int8) { + x: int8 &convert=($$ * mult); + on %done { print "%s: %d" % (msg, self.x); } +}; + +public type Foo = unit { + y: Bar("My multiplied integer", 5); +}; \ No newline at end of file diff --git a/doc/programming/examples/_unit-params.spicy.output b/doc/programming/examples/_unit-params.spicy.output new file mode 100644 index 000000000..5d37bcfbf --- /dev/null +++ b/doc/programming/examples/_unit-params.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- printf '\05' | spicy-driver %INPUT/printf '\05' | spicy-driver %INPUT/False +# printf '\05' | spicy-driver foo.spicy +"My multiplied integer": 25 diff --git a/doc/programming/examples/_unit-vars-optional.spicy b/doc/programming/examples/_unit-vars-optional.spicy new file mode 100644 index 000000000..165415e69 --- /dev/null +++ b/doc/programming/examples/_unit-vars-optional.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + on %init { print self; } + x: int8 { self.a = "Our integer is %d" % $$; } + on %done { print self; } + + var a: string &optional; +}; \ No newline at end of file diff --git a/doc/programming/examples/_unit-vars-optional.spicy.output b/doc/programming/examples/_unit-vars-optional.spicy.output new file mode 100644 index 000000000..0ebb7cce8 --- /dev/null +++ b/doc/programming/examples/_unit-vars-optional.spicy.output @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- printf \05 | spicy-driver %INPUT/printf \05 | spicy-driver %INPUT/False +# printf \05 | spicy-driver foo.spicy +[$x=(not set), $a=(not set)] +[$x=48, $a="Our integer is 48"] diff --git a/doc/programming/examples/_unit-vars.spicy b/doc/programming/examples/_unit-vars.spicy new file mode 100644 index 000000000..e426c9879 --- /dev/null +++ b/doc/programming/examples/_unit-vars.spicy @@ -0,0 +1,10 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +public type Foo = unit { + on %init { print self; } + x: int8 { self.a = "Our integer is %d" % $$; } + on %done { print self; } + + var a: string; +}; \ No newline at end of file diff --git a/doc/programming/examples/_unit-vars.spicy.output b/doc/programming/examples/_unit-vars.spicy.output new file mode 100644 index 000000000..a63c58a84 --- /dev/null +++ b/doc/programming/examples/_unit-vars.spicy.output @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- printf \05 | spicy-driver %INPUT/printf \05 | spicy-driver %INPUT/False +# printf \05 | spicy-driver foo.spicy +[$x=(not set), $a=""] +[$x=48, $a="Our integer is 48"] diff --git a/doc/programming/index.rst b/doc/programming/index.rst new file mode 100644 index 000000000..576fd1b39 --- /dev/null +++ b/doc/programming/index.rst @@ -0,0 +1,26 @@ + +.. _programming: + +==================== +Programming in Spicy +==================== + +This chapter summarizes the main concepts of writing Spicy grammars. We +begin with a deep-dive on Spicy's main task, parsing: We walk through +all the corresponding constructs & mechanisms available to grammar +writers, without paying too much attention to other specifics of the +Spicy language, such as data types and control flow constructs. Much +of that should be sufficiently intuitive to readers familiar with +standard scripting languages. However, once we finish the parsing +section, we follow up with a comprehensive overview of the Spicy +language, as well as a reference of pre-built library functionality +that Spicy grammars can leverage. + +.. toctree:: + :maxdepth: 2 + + parsing + language/index + library + examples + debugging diff --git a/doc/programming/language/error-handling.rst b/doc/programming/language/error-handling.rst new file mode 100644 index 000000000..ebe6b941b --- /dev/null +++ b/doc/programming/language/error-handling.rst @@ -0,0 +1,42 @@ + +.. _error_handling: + +=============== +Error Handling +=============== + +.. todo:: + + Spicy's error handling remains quite limited at this point, with + more to come here in the future. + +.. _exceptions: + +.. rubric:: Exceptions + +Exceptions provide Spicy's primary mechanism for reporting errors. +Currently, various parts of the runtime system throw exceptions if +they encounter unexpected situations. In particular, the generated +parsers throw ``ParsingError`` exceptions if they find themselves +unable to comprehend their input. However, the support for catching +and handling exception is remains minimal at the moment. For now, only +``ParsingError`` exceptions can be caught indirectly through the +:ref:`%on_error ` unit hook, which internally is nothing +else than an exception handler. + +.. todo:: + + Support for catching other exception throughs :ref:`statement_try` + needs to be added still (:issue:`89`). + +.. rubric:: ``result`` / ``error`` + +.. todo:: Spicy doesn't have ``result``/``error`` yet (:issue:`90`). + +.. rubric:: Error recovery + +.. todo:: + + The earlier Spicy prototype had support for resynchronizing + parsers with their input stream after parse error. It's on the + list to bring that back (:issue:`23`). diff --git a/doc/programming/language/examples/_function-inout.spicy b/doc/programming/language/examples/_function-inout.spicy new file mode 100644 index 000000000..1f075f00a --- /dev/null +++ b/doc/programming/language/examples/_function-inout.spicy @@ -0,0 +1,12 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +global s = "1"; + +function foo(inout x: string) { + x = "2"; +} + +print s; +foo(s); +print s; \ No newline at end of file diff --git a/doc/programming/language/examples/_function-inout.spicy.output b/doc/programming/language/examples/_function-inout.spicy.output new file mode 100644 index 000000000..c449144a4 --- /dev/null +++ b/doc/programming/language/examples/_function-inout.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- spicyc -j %INPUT//False +1 +2 diff --git a/doc/programming/language/examples/_module-hello-world.spicy b/doc/programming/language/examples/_module-hello-world.spicy new file mode 100644 index 000000000..a9f751a1f --- /dev/null +++ b/doc/programming/language/examples/_module-hello-world.spicy @@ -0,0 +1,4 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +print "Hello, world!"; \ No newline at end of file diff --git a/doc/programming/language/examples/_module-hello-world.spicy.output b/doc/programming/language/examples/_module-hello-world.spicy.output new file mode 100644 index 000000000..e0d457dc1 --- /dev/null +++ b/doc/programming/language/examples/_module-hello-world.spicy.output @@ -0,0 +1,3 @@ +# Automatically generated; do not edit. -- spicyc -j %INPUT/spicyc -j %INPUT/False +# spicyc -j hello-world.spicy +Hello, world! diff --git a/doc/programming/language/examples/_statement-for.spicy b/doc/programming/language/examples/_statement-for.spicy new file mode 100644 index 000000000..1b89c946e --- /dev/null +++ b/doc/programming/language/examples/_statement-for.spicy @@ -0,0 +1,14 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +for ( i in [1, 2, 3] ) + print i; + +for ( i in b"abc" ) { + print i; +} + +local v = vector("a", "b", "c"); + +for ( i in v ) + print i; \ No newline at end of file diff --git a/doc/programming/language/examples/_statement-for.spicy.output b/doc/programming/language/examples/_statement-for.spicy.output new file mode 100644 index 000000000..7a0425fc2 --- /dev/null +++ b/doc/programming/language/examples/_statement-for.spicy.output @@ -0,0 +1,11 @@ +# Automatically generated; do not edit. -- spicyc -j %INPUT/spicyc -j %INPUT/False +# spicyc -j for.spicy +1 +2 +3 +97 +98 +99 +a +b +c diff --git a/doc/programming/language/examples/_statement-interpolation.spicy b/doc/programming/language/examples/_statement-interpolation.spicy new file mode 100644 index 000000000..57ed77b5a --- /dev/null +++ b/doc/programming/language/examples/_statement-interpolation.spicy @@ -0,0 +1,5 @@ +# Automatically generated; edit in Sphinx source code, not here. +module Test; + +print "Hello, %!" % "World"; +print "%s=%d" % ("x", 1); \ No newline at end of file diff --git a/doc/programming/language/examples/_statement-interpolation.spicy.output b/doc/programming/language/examples/_statement-interpolation.spicy.output new file mode 100644 index 000000000..205ff40fc --- /dev/null +++ b/doc/programming/language/examples/_statement-interpolation.spicy.output @@ -0,0 +1,4 @@ +# Automatically generated; do not edit. -- spicyc -j %INPUT/spicyc -j %INPUT/False +# spicyc -j print.spicy +Hello, World +x=1 diff --git a/doc/programming/language/functions.rst b/doc/programming/language/functions.rst new file mode 100644 index 000000000..3ecbb74b2 --- /dev/null +++ b/doc/programming/language/functions.rst @@ -0,0 +1,50 @@ + +.. _functions: + +========= +Functions +========= + +Spicy's language allows to define custom functions just +like most other languages. The generic syntax for defining a function +with is ``N`` parameters is:: + + [public] function NAME(NAME_1: TYPE_1, ..., NAME_N: TYPE_N) [: RETURN_TYPE ] { + ... BODY ... + } + +A ``public`` function will be :ref:`accessible from other modules +` . If the return type is skipped, it's implicitly taken as +``void``, i.e., the function will not return anything. If a function +has return type other than void, all paths through the body must end +in a :ref:`statement_return` returning a corresponding value. + +A parameter specifications can be postfixed with a default value: +``NAME: TYPE = DEFAULT``. Callers may then skip providing that +parameter. + +By default, by parameters are passed by constant reference and hence +remain read-only inside the function's body. To make a parameter +modifiable, with any changes becoming visible to the caller, a +parameter can be prefixed with ``inout``: + +.. spicy-code:: function-inout.spicy + + module Test; + + global s = "1"; + + function foo(inout x: string) { + x = "2"; + } + + print s; + foo(s); + print s; + +.. spicy-output:: function-inout.spicy + :exec: spicyc -j %INPUT + +Spicy has couple more function-like constructs (:ref:`unit_hooks` and +:ref:`unit_parameters`) that use the same conventions for parameter +passing. diff --git a/doc/programming/language/index.rst b/doc/programming/language/index.rst new file mode 100644 index 000000000..515d118ec --- /dev/null +++ b/doc/programming/language/index.rst @@ -0,0 +1,16 @@ + +.. _spicy_language: + +======== +Language +======== + +.. toctree:: + :maxdepth: 2 + + modules + functions + variables + types + statements + error-handling diff --git a/doc/programming/language/modules.rst b/doc/programming/language/modules.rst new file mode 100644 index 000000000..db09286f9 --- /dev/null +++ b/doc/programming/language/modules.rst @@ -0,0 +1,62 @@ + +.. _modules: + +======= +Modules +======= + +Spicy source code is structured around modules, which essentially +introduce namespaces around other elements defined inside (e.g., +types, functions). Accordingly, all Spicy input files must start with +``module NAME;``, where ``NAME`` is scope that's being created. + +After that initial ``module`` statement, modules may contain arbitrary +list of declarations (types, globals, functions), as well as code +statement to execute. Any code defined at the global level will run +once at the module's initialization time. That's what gives us Spicy's +minimal ``hello-world`` module: + +.. spicy-code:: module-hello-world.spicy + + module Test; + + print "Hello, world!"; + +.. spicy-output:: module-hello-world.spicy + :show-with: hello-world.spicy + :exec: spicyc -j %INPUT + +.. _modules_import: + +To make the contents of another module accessible, Spicy provides an +``import NAME;`` statement that pulls in all public identifiers of the +specified external module. Spicy searches for ``NAME`` along it's +module search path. By default, that's the current directory plus the +location where Spicy's pre-built library modules are installed. To +find the module in one of those directories, its filename must be +``NAME.spicy``, with case-sensitive matching + +``spicy-config --libdirs`` shows the default search path. The Spicy +tools ``spicy`` && ``spicy-driver`` provide ``--library-path`` options +to add further custom directories. + +.. todo:: + + Actually ``spicy-driver`` does not have that option yet + (:issue:`88`). And we should also add an environment variable + ``SPICY_PATH``. + +There's a second version of the import statement that allows to import +from relative locations inside the search path: ``import NAME from +X.Y.Z;`` searches the module ``NAME`` (i.e., ``NAME.spicy``) inside a +sub-directory ``X/Y/Z`` along the search path. + +Once Spicy code has imported a module, it can access identifiers by +prefixing them with the module's namespace:: + + import MyModule; + + print MyModule::my_global_variable; + +Note that only identifiers declared as ``public`` become accessible +across module boundaries. diff --git a/doc/programming/language/statements.rst b/doc/programming/language/statements.rst new file mode 100644 index 000000000..8e2288da6 --- /dev/null +++ b/doc/programming/language/statements.rst @@ -0,0 +1,265 @@ + +.. _statements: + +========== +Statements +========== + +Most of Spicy's statements are pretty standard stuff. We summarize +them briefly in the following. + +.. _statement_assert: + +``assert`` +---------- + +:: + + assert EXPR; + + assert EXPR : MSG; + +Ensures at runtime that ``EXPR`` evaluates to a ``True`` value. If it +doesn't, an exception gets thrown that will typically abort execution. +``EXPR`` must either be of boolean type to begin with, or support +coercion into it. If ``MSG`` is specified, it must be a string and +will be carried along with the exception. + +.. _statement_break: + +``break`` +--------- + +:: + + break; + +Inside a :ref:`statement_for` or :ref:`statement_while` loop, +``break`` aborts the loop's body, with execution then continuing +right after the loop construct. + +.. _statement_for: + +``for`` +------- + +:: + + for ( ID in ITERABLE ) + BLOCK + +Loops over all the elements of an iterable value. ``ID`` is an +identifier that will become local variable inside ``BLOCK``, with the +current loop element assigned on each round. ``ITERABLE`` is a value +of any type that provides iterators. + +Examples: + +.. spicy-code:: statement-for.spicy + + module Test; + + for ( i in [1, 2, 3] ) + print i; + + for ( i in b"abc" ) { + print i; + } + + local v = vector("a", "b", "c"); + + for ( i in v ) + print i; + +.. spicy-output:: statement-for.spicy + :show-with: for.spicy + :exec: spicyc -j %INPUT + +.. _statement_if: + +``if`` +------ + +:: + + if ( EXPR ) + BLOCK + + if ( EXPR ) + BLOCK + else + BLOCK + +A classic ``if``-statement branching based on a boolean expression +``EXPR``. + +.. _statement_print: + +``print`` +--------- + +:: + + print EXPR; + + print EXPR_1, ..., EXPR_N; + +Prints one or more expressions to standard output. This is supported +for expressions of any type, with each type knowing how to render its +values into a readable representation. If multiple expressions are +specified, commas will separate them in the output. + +.. note:: + + A particular use-case combines ``print`` with string interpolation + (i.e., :spicy:op:`string::Modulo`): + + .. spicy-code:: statement-interpolation.spicy + + module Test; + + print "Hello, %!" % "World"; + print "%s=%d" % ("x", 1); + + .. spicy-output:: statement-interpolation.spicy + :show-with: print.spicy + :exec: spicyc -j %INPUT + +.. _statement_return: + +``return`` +---------- + +:: + + return; + + return EXPR; + +Inside a function or hook, ``return`` yields control back to the +caller. If it's a function with a non-void return value, the +return must provide a corresponding ``EXPR``. + +.. _statement_stop: + +``stop`` +-------- + +:: + + stop; + +Inside a ``foreach`` container hook (see :ref:`here `), aborts +the parsing loop without adding the current (final) value to the +container. + +.. _statement_switch: + +``switch`` +---------- + +:: + + switch ( [local IDENT =] CTRL_EXPR ) { + case EXPR [, ..., EXPR]: + BLOCK; + + ... + + case EXPR [, ..., EXPR]: + BLOCK; + + [default: + BLOCK] + } + +.. _statement_throe: + +Branches across a set of alternatives based on the value of an control +expression. ``CTRL_EXPR`` is compared against all the ``case`` +expressions through the type's equality operator, coercing +``CTRL_EXPR`` accordingly first where necessary. If ``local IDENT`` is +specified, the blocks have access to a corresponding local variable +that holds the value of the control expression. If no ``default`` is +given, the runtime will throw a ``UnhandledSwitchCase`` exception if +there's no matching case. + +.. note:: + + Don't confuse the ``switch`` statement with the unit type's + :ref:`switch parsing construct `. They look similar, + but do different things. + +.. _statement_throw: + +``throw`` +--------- + +.. todo:: This isn't available in Spicy yet (:issue:`89`). + +:: + + throw EXPR; + + throw; # only inside "catch" + + +Throws an exception, letting execution stop at the current location +and propagating ``EXPR`` up the call stack to the nearest matching +:ref:`statement_try`. If not match is found, the exception eventually +bubbles up to the host application. If that doesn't catch it either, +the runtime will abort execution. ``EXPR`` must be of type +:ref:`type_exception`. ``EXPR`` can be skipped to rethrow the current +exception inside a ``catch`` block. + +.. _statement_try: + +``try/catch`` +------------- + +.. todo:: This isn't available in Spicy yet (:issue:`89`). + +:: + + try + BLOCK + + catch [(TYPE IDENT)] + BLOCK + + ... + + catch [(TYPE IDENT)] + BLOCK + +Catches any exception thrown in the ``try`` block that match one of +the types in any of ``catch`` headers, which must be +:ref:`type_exception` types. A ``catch`` without a type matches any +exception. If no ``catch`` matches an exception thrown in the ``try`` +block, it'll be propagated further up the stack. A bare ``throw`` +statement can be used inside a ``catch`` block to rethrow the current +exception. + +.. _statement_while: + +``while`` +--------- + +:: + + while ( COND ) + BLOCK + [else + BLOCK] + + while ( local IDENT = EXPR; COND ) + BLOCK + [else + BLOCK] + +A while-loop that executed ``BLOCK`` for as long as the boolean +``COND`` evaluates to true. The second form initializes a new local +variable ``IDENT`` with ``EXPR``, and makes it available inside both +``COND`` and the body. If an ``else`` block is specified, that will +executes once the ``COND`` evaluates to false at the loop beginning. +It will not execute if the loop aborts with a :ref:`statement_break`. diff --git a/doc/programming/language/types.rst b/doc/programming/language/types.rst new file mode 100644 index 000000000..01d003d82 --- /dev/null +++ b/doc/programming/language/types.rst @@ -0,0 +1,367 @@ + +.. _types: + +===== +Types +===== + +.. _type_address: + +Address +------- + +The address type stores both IPv4 and IPv6 addresses. + +.. rubric:: Type + +- ``addr`` + +.. rubric:: Constants + +- IPv4: ``1.2.3.4`` +- IPv6: ``[2001:db8:85a3:8d3:1319:8a2e:370:7348]``, ``[::1.2.3.4]`` + +.. include:: /../html/autogen/types/address.rst + +.. _type_bitfield: + +Bitfield +-------- + +Bitfields provide access to individual bitranges inside an unsigned +integer. That can't be instantiated directly, but must be defined and +parsed inside a unit. + +.. rubric:: Type + +- ``bitfield(N) { RANGE_1; ...; RANGE_N }`` +- Each ``RANGE`` has one of the forms ``LABEL: A`` or ``LABEL: A..B`` + where ``A`` and ``B`` are a bit numbers. + +.. include:: /../html/autogen/types/bitfield.rst + +.. _type_bool: + +Bool +---- + +Boolean values can be true or false. + +.. rubric:: Type + +- ``bool`` + +.. rubric:: Constants + +- ``True``, ``False`` + +.. include:: /../html/autogen/types/bool.rst + +.. _type_bytes: + +Bytes +----- + +Bytes instances store raw, opaque data. They provide iterators to +traverse their content. + +.. rubric:: Types + +- ``bytes`` +- ``iterator`` + +.. rubric:: Constants + +- ``b"Spicy"``, ``b""`` + +.. include:: /../html/autogen/types/bytes.rst +.. include:: /../html/autogen/types/bytes-iterator.rst + +.. _type_enum: + +Enum +---- + +Enum types associate labels with a numerical values. + +.. rubric:: Type + +- ``enum { LABEL_1; ... LABEL_N }`` +- Each label has the form ``ID [= VALUE]``. If ``VALUE`` is skipped, + one will be assigned automatically. + +- Each enum type comes with an implicitly defined ``Undef`` label with + a value distinct from all other ones. When coerced into a boolean, + an enum will be true iff it's not ``Undef``. + +.. note:: An instance of a enum can assume a numerical value that does + not map to any of its defined label. If printed, it will render + into ```` in that case, with ``N`` being its value. + +.. rubric:: Constants + +- The individual labels represent constants of the corresponding type + (e.g., ``MyEnum::MyFirstLabel`` is a constant of type ``MyEnum``). + +.. include:: /../html/autogen/types/enum.rst + +.. _type_exception: + +Exception +--------- + +.. todo:: This isn't available in Spicy yet (:issue:`89`). + +.. _type_integer: + +Integer +------- + +Spicy distinguishes between signed and unsigned integers, and always +requires specifying the bitwidth of a type. + +.. rubric:: Type + +- ``intN`` for signed integers, where ``N`` can be one of 8, 16, 32, 64. +- ``uintN`` for signed integers, where ``N`` can be one of 8, 16, 32, 64. + +.. rubric:: Constants + +- Unsigned integer: ``1234``, ``+1234``, ``uint8(42)``, ``uint16(42)``, ``uint32(42)``, ``uint64(42)`` +- Signed integer: ``-1234``, ``int8(42)``, ``int8(-42)``, ``int16(42)``, ``int32(42)``, ``int64(42)`` + +.. include:: /../html/autogen/types/integer.rst + +.. _type_port: + +Port +---- + +Ports represent the combination of a numerical port number and an +associated transport-layer protocol. + +.. rubric:: Type + +- ``port`` + +.. rubric:: Constants + +- ``443/tcp``, ``53/udp`` + +.. include:: /../html/autogen/types/port.rst + +.. _type_real: + +Real +---- + +"Real" values store floating points with double precision. + +.. rubric:: Type + +- ``real`` + +.. rubric:: Constants + +- ``3.14``, ``10e9``, ``0x1.921fb78121fb8p+1`` + +.. include:: /../html/autogen/types/real.rst + +.. _type_regexp: + +Regular Expression +------------------ + +Spicy's provide POSIX-style regular expressions. + +.. rubric:: Type + +- ``regexp`` + +.. rubric:: Constants + +- ``/Foo*ba?r/``, ``/X(..)(..)(..)Y/`` + +Regular expressions use the extended POSIX syntax, with a few smaller +differences and extensions: + +- Supported character classes are: ``[:lower:]``, ``[:upper:]``, + ``[:digit:]``, ``[:upper:]``, ``[:blank:]``. +- ``\b`` asserts a word-boundary, ``\B`` matches asserts no word + boundary. +- ``{#}`` associates a numerical ID with a regular expression + (useful for set matching). + + +.. include:: /../html/autogen/types/regexp.rst + +.. _type_sink: + +Sink +---- + +Sinks act as a connector between two units, facilitating feeding the +output of one as input into the other. See :ref:`sinks` for a full +description. + +Sinks are special in that they don't represent a type that's generally +available for instantiation. Instead they need to be declared as the +member of unit using the special ``sink`` keyword. You can, however, +maintain references to sinks by assigning the unit member to a variable +of type ``Sink&``. + +.. include:: /../html/autogen/types/sink.rst + +.. rubric: Hooks + +Sinks provide a set of dedicated unit hooks as callbacks for the +reassembly process. These must be implemented on the reader side, +i.e., the unit that's connected to a sink. + +.. spicy:method:: %on_gap sink %on_gap False - (seq: uint64, len: uint64) + +.. spicy:method:: %on_overlap sink %on_overlap False - (seq: uint64, old: data, new: data) + +Triggered when reassembly encounters a 2nd version of data for +sequence space already covered earlier. *seq* is the start of the +overlap, and *old*/*new* the previous and the new data, respectively. +This hook is just for informational purposes, the policy set with +:spicy:method:`sink::set_policy` determines how the reassembler +handles the overlap. + +.. spicy:method:: %on_skipped sink %on_skipped False - (seq: uint64) + +Any time :spicy:method:`sink::skip` moves ahead in the input stream, this hook reports +the new sequence number *seq*. + +.. spicy:method:: %on_undelivered sink %on_skipped False - (seq: uint64, data: bytes) + +If data still buffered is skipped over through +:spicy:method:`sink::skip` , it will be passed to this hook before +being adjusting the current position. *seq* the starting sequence +number of the data, *data* the data itself. + +.. _type_stream: + +Stream +------ + +A ``stream`` is data structure aimed at efficiently representing a +potentially large, incrementally built input stream of raw data. You +can think of it as a :ref:`type_bytes` type that's optimized for (1) +efficiently appending new chunks of data at the end, and (2) trimming +data no longer needed at the beginning. Other than those two +operation, stream data cannot be modified; there's no way to change +the actual content of a stream once it has been added. Streams +provides *iterators* for traversal, and *views* for limiting +visibility to a smaller window into the total stream. + +Streams are key to Spicy's parsing process, although most of that +happens behind the scenes. Most likely you will encounter them when +using :ref:`random_access`. They may also be useful for buffering +larger volumes of data during processing. + +.. rubric:: Types + +- ``stream`` +- ``iterator`` +- ``view`` + +.. include:: /../html/autogen/types/stream.rst +.. include:: /../html/autogen/types/stream-iterator.rst +.. include:: /../html/autogen/types/stream-view.rst + +.. _type_string: + +String +------ + +Strings store readable text that's associated with a given character +set. Internally, Spicy stores them as UTF-8. + +.. rubric:: Type + +- ``string`` + +.. rubric:: Constants + +- ``"Spicy"``, ``""`` +- When specifying string constants, Spicy assumes them to be in UTF-8. + +.. include:: /../html/autogen/types/string.rst + +.. _type_tuple: + +Tuple +----- + +Tuples are heterogeneous containers of a fixed, ordered set of types. + +.. rubric:: Type + +- ``tuple`` specifies a vector with elements of type ``T``. +- ``iterator>`` + +.. rubric:: Constants + +- ``vector(E_1, E_2, ..., E_N)`` creates a vector of ``N`` elements. + The values ``E_I`` must all have the same type. ``vector()`` creates + an empty vector of unknown element type; this cannot be used + directly but must be coerced into a fully-defined vector type first. + +- Vectors can be initialized through coercions from a list value: + ``vector I = ["A", "B", "C"]``. + +.. include:: /../html/autogen/types/vector.rst +.. include:: /../html/autogen/types/vector-iterator.rst + +.. _type_void: + +Void +---- + +The void type is place holder for specifying "no type", such as when a +function doesn't return anything. + +.. rubric:: Type + +- ``void`` diff --git a/doc/programming/language/variables.rst b/doc/programming/language/variables.rst new file mode 100644 index 000000000..096e62fd6 --- /dev/null +++ b/doc/programming/language/variables.rst @@ -0,0 +1,39 @@ + +.. _variables: + +======================= +Variables and Constants +======================= + +At the global module level, we declare variables with the ``global`` +keyword:: + + global NAME: TYPE [= DEFAULT]; + +This defines a global variable called ``NAME`` with type ``TYPE``. If +a default is giving, Spicy initialized the global accordingly before +any code executes. Otherwise, the global received a type-specific +default, typically the type's notion of a null value. As a result, +globals are always initialized to a well-defined value. + +As a shortcut, you can skip ``: TYPE`` if the global comes with a +default.Spicy then just applies the expression's type to the global. + +We define global constants in a similar way, just replacing ``global`` +with ``const``:: + + const x: uint32 = 42; + const foo = "Foo"; + +Inside a function, local variables use the same syntax once more, just +prefixed with ``local`` this time:: + + function f() { + local x: bytes; + local y = "Y"; + + } + +Usual scoping rules apply to locals. Just like globals, locals are +always initialized to a well-defined value: either their default if +given, or the type's null value. diff --git a/doc/programming/library.rst b/doc/programming/library.rst new file mode 100644 index 000000000..ee46cd02f --- /dev/null +++ b/doc/programming/library.rst @@ -0,0 +1,33 @@ + +.. _library: + +======= +Library +======= + +Module ``spicy`` +---------------- + +.. _library_types: + +Types +~~~~~ + +.. include:: /../html/autogen/spicy-types.spicy + +.. _library_functions: + +Functions +~~~~~~~~~ + +.. include:: /../html/autogen/spicy-functions.spicy + +.. _library_filters: + +Module ``filter`` +----------------- + +Types +~~~~~ + +.. include:: /../html/autogen/filter-types.spicy diff --git a/doc/programming/parsing.rst b/doc/programming/parsing.rst new file mode 100644 index 000000000..4dbf323ed --- /dev/null +++ b/doc/programming/parsing.rst @@ -0,0 +1,1371 @@ + +.. _parsing: + +======= +Parsing +======= + +Basics +====== + +.. rubric:: Type Declaration + +Spicy expresses units of data to parse through a type called, +appropriately, ``unit``. At a high level, a unit is similar to structs +or records in other languages: It defines an ordered set of fields, +each with a name and a type, that during runtime will store +corresponding values. Units can be instantiated, fields can be +assigned values, and these values can then be retrieved. Here's about +the most basic Spicy unit one can define:: + + type Foo = unit { + version: uint32; + }; + +We name the type ``Foo``, and it has just one field called +``version``, which stores a 32-bit unsigned integer type. + +Leaving parsing aside for a moment, we can indeed use this type +similar to a typical struct/record type: + +.. spicy-code:: basic-unit-module.spicy + + module Test; + + type Foo = unit { + version: uint32; + }; + + global f: Foo; + f.version = 42; + print f; + +This will print: + +.. spicy-output:: basic-unit-module.spicy + :exec: spicyc -j %INPUT + +Fields are initially unset, and attempting to read an unset field will +trigger a :ref:`runtime exception `. You may, however, +provide a default value by adding a `&default` *attribute* to the +field, in which case that will be returned on access if no value has +been explicitly assigned: + +.. spicy-code:: basic-unit-module-with-default.spicy + + module Test; + + type Foo = unit { + version: uint32 &default=42; + }; + + global f: Foo; + print f; + print "version is %s" % f.version; + +This will print: + +.. spicy-output:: basic-unit-module-with-default.spicy + :exec: spicyc -j %INPUT + +Note how the field remains unset even with the default now specified, +while the access returns the expected value. + +.. rubric:: Parsing a Field + +We can turn this minimal unit type into a starting point for parsing +data---in this case a 32-bit integer from four bytes of raw input. +First, we need to declare the unit as ``public`` to make it accessible +from outside of the current module---a requirement if a host +application wants to use the unit as a parsing entry point. + +.. spicy-code:: basic-unit-parse.spicy + + module Test; + + public type Foo = unit { + version: uint32; + + on %done { + print "0x%x" % self.version; + } + }; + +Let's use :ref:`spicy-driver` to parse 4 bytes of input through this +unit: + +.. spicy-output:: basic-unit-parse.spicy + :exec: printf '\01\02\03\04' | spicy-driver %INPUT + :show-with: foo.spicy + +The output comes of course from the ``print`` statement inside the +``%done`` hook, which executes once the unit has been fully parsed. +(We will discuss unit hooks further below.) + +By default, Spicy assumes integers that it parses to be represented in +network byte order (i.e., big-endian), hence the output above. +Alternatively, we can tell the parser through an attribute that our +input is arriving in, say, little-endian instead. To do that, we +import the ``spicy`` library module, which provides an enum type +:ref:`spicy_byteorder` that we can give to a ``&byte-order`` field +attribute that integer fields support: + +.. spicy-code:: basic-unit-parse-byte-order.spicy + + module Test; + + import spicy; + + public type Foo = unit { + version: uint32 &byte-order=spicy::ByteOrder::Little; + + on %done { + print "0x%x" % self.version; + } + }; + +.. spicy-output:: basic-unit-parse-byte-order.spicy + :exec: printf '\01\02\03\04' | spicy-driver %INPUT + :show-with: foo.spicy + +We see that unpacking the value has now flipped the bytes before +storing it in the ``version`` field. + +Similar to ``%byte-order``, Spicy offers a variety of further +attributes that control the specifics of how fields are parsed. We'll +discuss them in the relevant sections throughout the rest of this +chapter. + +.. rubric:: Non-type Fields + +Unit fields always have a type. However, in some cases a field's type +is not explicitly declared, but derived from what's being parsed. The +main example of this is parsing a constant value: Instead of a type, a +field can specify a constant of a parseable type. The field's type +will then (usually) just correspond to the constant's type, and +parsing will expect to find the corresponding value in the input +stream. If a different value gets unpacked instead, parsing will abort +with an error. Example: + +.. spicy-code:: constant-field.spicy + + module Test; + + public type Foo = unit { + bar: b"bar"; + on %done { print self.bar; } + }; + +.. spicy-output:: constant-field.spicy 1 + :exec: printf 'bar' | spicy-driver %INPUT + :show-with: foo.spicy + +.. spicy-output:: constant-field.spicy 2 + :exec: printf 'foo' | spicy-driver %INPUT + :show-with: foo.spicy + :expect-failure: + +:ref:`Regular expressions ` extend this scheme a bit +further: If a field specifies a regular expression constant rather +than a type, the field will have have type :ref:`type_bytes` and store +the data that ends up matching the regular expression: + +.. spicy-code:: regexp.spicy + + module Test; + + public type Foo = unit { + x: /Foo.*Bar/; + on %done { print self; } + }; + +.. spicy-output:: regexp.spicy + :exec: printf 'Foo12345Bar' | spicy-driver %INPUT + :show-with: foo.spicy + +There's also a programmatic way to change a field's type to something +that's different than what's being parsed, see the +:ref:`attribute_convert`. + +.. rubric:: Anonymous Fields + +Field names are optional. If skipped, the field becomes an *anonymous* +field. These still participate in parsing as any other field, but they +won't store any value, nor is there a way to get access to them from +outside. You can however still get to the parsed value inside a +corresponding field hook (see :ref:`unit_hooks`) using the reserved +``$$`` identifier (see :ref:`id_dollardollar`). + +.. spicy-code:: anonymous-field.spicy + + module Test; + + public type Foo = unit { + x: int8; + : int8 { print $$; } # anonymous field + y: int8; + on %done { print self; } + }; + +.. spicy-output:: anonymous-field.spicy + :exec: printf '\01\02\03' | spicy-driver %INPUT + :show-with: foo.spicy + +.. _id_dollardollar: +.. _id_self: + +.. rubric:: Reserved Identifiers + +Inside units, two reserved identifiers provide access to values +currently being parsed: + +``self`` + Inside a unit's type definition, ``self`` refers to the unit + instance that's currently being processed. The instance is + writable and maybe modified by assigning to any fields of + ``self``. + +``$$`` + Inside field attributes and hooks, ``$$`` refers to the just + parsed value, even if it's not going to be directly stored in the + field. The value of ``$$`` is writable and may be modified. + +.. _attribute_convert: + +.. rubric:: On-the-fly Type Conversion with ``&convert`` + +Fields may use an attribute ``&convert=EXPR`` to transform the value +that was just being parsed before storing it as the field's final +value. With the attribute being present, it's the value of ``EXPR`` +that's stored in the field, not the parsed value. Accordingly, the +field's type also changes to the type of ``EXPR``. + +Typically, ``EXPR`` will use ``$$`` to access the value actually being +parsed and then transform it into the desired representation. For +example, the following stores an integer parsed in an ASCII +representation as a ``uint64``: + +.. spicy-code:: parse-convert.spicy + + module Test; + + import spicy; + + public type Foo = unit { + x: bytes &eod &convert=$$.to_uint(); + on %done { print self; } + }; + +.. spicy-output:: parse-convert.spicy + :exec: printf 12345 | spicy-driver %INPUT + :show-with: foo.spicy + + +.. _unit_hooks: + +Unit Hooks +=========== + +Unit hooks provide one of the most powerful Spicy tools to control +parsing, track state, and retrieve results. Generally, hooks are +blocks of code triggered to execute at certain points during parsing, +with access to the current unit instance. + +Conceptually, unit hooks are somewhat similar to methods: They have +bodies that execute when triggered, and these bodies may receive a set +of parameters as input. Different from functions, however, a hook can +have more than one body. If multiple implementations are provided for +the same hook, all of them will execute successively. A hook may also +not have any body implemented at all, in which case there's nothing to +do when it executes. + +The most commonly used hooks are: + +``on %init() { ... }`` + Executes just before unit parsing will start. + +``on %done { ... }`` + Executes just after unit parsing has completed. + +.. _on_error: + +``on %error { ... }`` + Executes when a parse error has been encountered, just before the + parser either aborts processing. + +``on { ... }`` (field hook) + Executes just after the given unit field has been parsed. The + parsed value is accessible through the ``$$`` identifier. It will + also have been assigned to the field already, potentially with any + relevant type conversion applied (see :ref:`attribute_convert`). + +.. _foreach: + +``on foreach { ... }`` (container hook) + Assuming the specified field is a container (e.g., a vector), this + executes each time a new container element has been parsed, and + just before it's been added to the container. The parsed element + is accessible through the ``$$`` identifier, and can be modified + before it's stored. The hook implementation may also use the + :ref:`statement_stop` statement to abort container parsing, + without the current element being added anymore. + +In addition, Spicy provides a set of hooks specific to the ``sink`` +type; we discuss these the :ref:`corresponding section `. + +There are three location where hooks can be implemented: + +- Inside a unit, ``on { ... }`` implements the hook of the + given name:: + + type Foo = unit { + x: uint32; + v: unint8[]; + + on %init { ... } + on x { ... } + on v foreach { ... } + on %done { ... } + } + +- Field and container hooks may be directly attached to their field, + skipping the ``on ...`` part:: + + type Foo = unit { + x: uint32 { ... } + v: unint8[] foreach { ... } + } + +- At the global module level, one can add hooks to any available unit + type through ``on :: { ... }``. With the + definition of ``Foo`` above, this implements hooks externally:: + + on Foo::%init { ... } + on Foo::x { ... } + on Foo::v foreach { ... } + on Foo::%done { ... } + + External hooks work across module boundaries by qualifying the unit + type accordingly. They provide a powerful mechanism to extend a + predefined unit without changing any of its code. + +.. note:: + + When a hook executes, it has access to the current unit instance + through the ``self`` identifier. The state of that instance will + reflect where parsing is at at that time. In particular, any field + that haven't been parsed yet, will remain unset. (You can use the + ``?.`` unit operator to test if a field has received a value yet.) + +Unit Variables +============== + +In addition to unit field for parsing, you can also add further instance +variables to a unit type to store arbitrary state: + +.. spicy-code:: unit-vars.spicy + + module Test; + + public type Foo = unit { + on %init { print self; } + x: int8 { self.a = "Our integer is %d" % $$; } + on %done { print self; } + + var a: string; + }; + +.. spicy-output:: unit-vars.spicy + :exec: printf \05 | spicy-driver %INPUT + :show-with: foo.spicy + +Here, we assign a string value to ``a`` once we have parsed ``x`. The +final ``print`` shows the expected value. As you can also see, before +we assign anything, the variable's value is just empty: Spicy +initializes instances variables with well-defined defaults. If you +would rather leave a variable unset by default, you can add +`&optional`: + +.. spicy-code:: unit-vars-optional.spicy + + module Test; + + public type Foo = unit { + on %init { print self; } + x: int8 { self.a = "Our integer is %d" % $$; } + on %done { print self; } + + var a: string &optional; + }; + +.. spicy-output:: unit-vars-optional.spicy + :exec: printf \05 | spicy-driver %INPUT + :show-with: foo.spicy + +.. _unit_parameters: + +Unit Parameters +=============== + +Unit types can receive parameters upon instantion, which will then be +available to any code inside the type's declaration: + +.. spicy-code:: unit-params.spicy + + module Test; + + type Bar = unit(msg: string, mult: int8) { + x: int8 &convert=($$ * mult); + on %done { print "%s: %d" % (msg, self.x); } + }; + + public type Foo = unit { + y: Bar("My multiplied integer", 5); + }; + +.. spicy-output:: unit-params.spicy + :exec: printf '\05' | spicy-driver %INPUT + :show-with: foo.spicy + +This example shows a typical idiom: We're handing parameters down to a +subunit through parameters it receives. Inside the submodule, we then +have access to the values passed in. + +.. note:: It's usually not very useful to define a top-level parsing + unit with parameters because we don't have a way to pass anything + in through ``spicy-driver``. A custom host application could make + use of them, though. + +This works with subunits inside containers as well, though the +syntax is a bit peculiar: + +.. spicy-code:: unit-params-vector.spicy + + module Test; + + type Bar = unit(mult: int8) { + x: int8 &convert=($$ * mult); + on %done { print self.x; } + }; + + public type Foo = unit { + x: int8; + y: (Bar(self.x))[]; # Element constructor must be in "(...)" + }; + +.. spicy-output:: unit-params-vector.spicy + :exec: printf '\05\01\02\03' | spicy-driver %INPUT + :show-with: foo.spicy + +Unit parameters follow the same passing conventions as :ref:`function +parameters `. In particular, they are read-only by default. +If the subunit wants to modify a parameter it receives, it needs +to be declared as ``inout`` (e.g., ``Bar(inout s: string)``). + +.. note:: + + A common use-case for unit parameters is passing the ``self`` of a + higher-level unit down into a subunit:: + + type Foo = unit { + ... + b: Bar(self); + ... + } + + type Bar = unit(foo: Foo) { + # We now have access to any state in "foo". + } + + That way, the subunit can for example store state directly in the + parent. + +.. _unit_meta_data: + +Meta data +========= + +Units can provide meta data about their semantics through *properties* +that both Spicy itself and host applications can access. One defines +properties inside the unit's type as ``% = `` tuples. +Currently, the following properties are defined: + +``%mime-type`` + A string of the form ``"/"`` that defines the MIME + type for content the unit knows how to parse. This may include a + ``*`` wildcard for either the type or subtype. We use a + generalized notion of MIME types here that can include custom + meanings. See :ref:`sinks` for more on how these MIME types are + used to select parsers dynamically during runtime. + + You can specify this property more than once to associate a unit + with multiple types. + +``%port`` + A :ref:`type_port` to associate this unit with. This property has + no built-in effect, but host applications may use of the + information to decide which unit type to use for parsing a + connection's payload. + +``%description`` + A short textual description of the unit type (i.e., the parser + that it defines). Host applications have access to this property, + and ``spicy-driver`` includes the information into the list of + available parsers that it prints with the ``--list-parsers`` + option. + +Parsing Types +============= + +Several, but not all, of Spicy's :ref:`data types ` can be +parsed from binary data. In the following we summary the types that +can, along with any options they support to control specifics of how +they unpack binary representations. + +.. _parse_address: + +Address +^^^^^^^ + +Spicy parses :ref:`addresses ` from either 4 bytes of +input for IPv4 addresses, or 16 bytes for IPv6 address. To select the +type, a unit field of type ``address`` must come with either an +``&ipv4`` or ``&ipv6`` attribute. + +By default, addresses are assumed to be represented in network-byte +order. Alternatively, a different byte order can be specified through +a ``&byte-order`` attribute specifying the desired +:ref:`spicy_byteorder`. + +Example: + +.. spicy-code:: parse-address.spicy + + module Test; + + import spicy; + + public type Foo = unit { + ip: addr &ipv6 &byte-order=spicy::ByteOrder::Little; + on %done { print self; } + }; + +.. spicy-output:: parse-address.spicy + :exec: printf '1234567890123456' | spicy-driver %INPUT + :show-with: foo.spicy + +.. _parse_bitfield: + +Bitfield +^^^^^^^^ + +Bitfields parse an integer value of a given size, and then make +selected smaller bit ranges within that value available individually +through dedicated identifiers. For example, the following unit parses +4 bytes as an ``uint32`` and makes the value of bit 0 available as +``f.x1``, bits 1 to 2 as ``f.x2``, and bits 3 to 5 as ``f.x3``, +respectively: + +.. spicy-code:: parse-bitfield.spicy + + module Test; + + public type Foo = unit { + f: bitfield(32) { + x1: 0; + x2: 1..2; + x3: 3..4; + }; + + on %done { + print self.f.x1, self.f.x2, self.f.x3; + print self; + } + }; + +.. spicy-output:: parse-bitfield.spicy + :exec: printf '\01\02\03\04' | spicy-driver %INPUT + :show-with: foo.spicy + +Generally, a field ``bitfield(N)`` field is parsed like an +``uint``. The field then supports dereferencing individual bit +ranges through their labels. The corresponding expressions +(``self.x.``) have the same ``uint`` type as the parsed value +itself, with the value shifted to the right so that the lowest +extracted bit becomes bit 0 of the returned value. As you can see in +the example, the type of the field itself becomes a tuple composed of +the values of the individual bit ranges. + +By default, a bitfield assumes the underlying integer comes in network +byte order. You can specify a ``&byte-order`` attribute to change that +(e.g., ``bitfield(32) { ... } &byte-order=spicy::ByteOrder::Little``). +Furthermore, each bit range can also specify a ``&bit-order`` +attribute to specify the :ref:`ordering ` for its +bits; the default is ``spicy::BitOrder::LSB0``. + +The individual bit ranges support the ``&convert`` attribute and will +adjust their types accordingly, just like a regular unit field (see +:ref:`attribute_convert`). For example, that allows for mapping a bit +range to an enum, using ``$$`` to access the parsed value: + +.. spicy-code:: parse-bitfield-enum.spicy + + module Test; + + import spicy; + + type X = enum { A = 1, B = 2 }; + + public type Foo = unit { + f: bitfield(8) { + x1: 0..3 &convert=X($$); + x2: 4..7 &convert=X($$); + } { print self.f.x1, self.f.x2; } + }; + +.. spicy-output:: parse-bitfield-enum.spicy + :exec: printf '\41' | spicy-driver %INPUT + :show-with: foo.spicy + +.. _parse_bytes: + +Bytes +^^^^^ + +When parsing a field of type :ref:`type_bytes`, Spicy will consume raw +input bytes according to a specified attribute that determines when to +stop. The following attributes are supported: + +``&eod`` + Consumes all subsequent data until the end of the input is reached. + +``&size=N`` + Consumes exactly ``N`` bytes. + +``&until=DELIM`` + Consumes bytes until the specified delimiter is found. ``DELIM`` + must be of type ``bytes`` itself. + +One of ``&eod``/``&size``/``&until`` must be given. + +On top of that, bytes fields support the attribute ``&chunked`` to +change how the parsed data is processed and stored. Normally, a bytes +field will first accumulate all desired data and then store the final, +complete value in the field. With ``&chunked``, if the data arrives +incrementally in pieces, the field instead processes just whatever is +available at a time, storing each piece directly, and individually, in +the field. Each time a piece gets stored, any associated field hooks +execute with the new part as their ``$$``. Parsing with ``&chunked`` +will eventually still consume the same number of bytes overall, but it +avoids buffering everything in cases where that's either infeasible or +simply not not needed. + +Bytes fields support parsing constants: If a ``bytes`` constant is +specified instead of a field type, parsing will expect to find the +corresponding value in the input stream. + +.. _parse_integer: + +Integer +^^^^^^^ + +Fields of :ref:`integer type ` can be either signed +(``intN``) or unsigned (``uintN``). In either case, the bit length +``N`` determines the number of bytes being parsed. By default, +integers are expected to come in network byte order. You can specify a +different order through the ``&byte-order=ORDER`` attribute, where +``ORDER`` is of type :ref:`spicy_ByteOrder`. + +Integer fields support parsing constants: If an integer constant is +specified instead the instead of a field type, parsing will expect to +find the corresponding value in the input stream. Since the exact type +of the integer constant is important, you should use their constructor +syntax to make that explicit (e.g., ``uint32(42)``, ``int8(-1)``; vs. +using just ``42`` or ``-1``). + +.. _parse_real: + +Real +^^^^ + +Real values are parsed as either single or double precision values in +IEEE754 format, depending on the value of their ``&type=T`` attribute, +where ``T`` is one of :ref:`spicy_RealType`. + +.. _parse_regexp: + +Regular Expression +^^^^^^^^^^^^^^^^^^ + +When parsing a field through a :ref:`type_regexp` , the expression is +expected to match at the current position of the input stream. The +field's type becomes ``bytes``, and it will store the matching data. +Matching is non-greedy(!): the first (shortest) match will satisfy the +parser. + +.. _parse_unit: + +Unit +^^^^ + +Fields can have the type of another unit, in which case parsing will +descend into that subunit's grammar until that instance has been fully +parsed. Field initialization and hooks work as usual. + +If the subunit receives parameters, they must be given right after the +type. + +.. spicy-code:: parse-unit-params.spicy + + module Test; + + type Bar = unit(a: string) { + x: uint8 { print "%s: %u" % (a, self.x); } + }; + + public type Foo = unit { + y: Bar("Spicy"); + on %done { print self; } + }; + +.. spicy-output:: parse-unit-params.spicy + :exec: printf '\01\02' | spicy-driver %INPUT + :show-with: foo.spicy + +See :ref:`unit_parameters` for more. + +.. _parse_vector: + +Vector +^^^^^^ + +Parsing a :ref:`vector ` creates a loop that repeatedly +parses elements of the specified type from the input stream until an +end condition is reached. The field's value accumulates all the +elements into the final vector. + +Spicy uses a specific syntax to define fields of type vector:: + + NAME : ELEM_TYPE[SIZE]. + +``NAME`` is the field name as usual. ``ELEM_TYPE`` is type of the +vector's elements, i.e., the type that will be repeatedly parsed. +``SIZE`` is the number of elements to parse into the vector; this is +an arbitrary Spicy expression yielding an integer value. The resulting +field type then will be ``vector``. Here's a simple example +parsing five ``uint8``: + +.. spicy-code:: parse-vector.spicy + + module Test; + + public type Foo = unit { + x: uint8[5]; + on %done { print self; } + }; + +.. spicy-output:: parse-vector.spicy + :exec: printf '\01\02\03\04\05' | spicy-driver %INPUT + :show-with: foo.spicy + +It is possible to skip the ``SIZE`` (e.g., ``x: uint8[]``) and instead +use another kind of end conditions to terminate a vector's parsing +loop. To that end, vectors support the following attributes: + +``&size=N`` + Parses the vector from the subsequent ``N`` bytes of input data. + This effectively limits the available input to the corresponding + window, letting the vector parse elements until it runs out of + data. + +``&until=VAL`` + Parses elements until one with the value ``VAL`` is encountered. + ``VAL`` must be of the same type as the vector's elements. Once + the specified element is encountered, vector parsing stops + *without* including the matching one into the field's vector + value. + +``&until_including=VAL`` + Similar to ``&until``, but does include the final element ``VAL`` + into the field's vector when stopping parsing. + +``&while=EXPR`` + Continues parsing as long as the boolean expression ``EXPR`` + evaluates to true. + +If neither a size nor an attribute is given, Spicy will attempt to use +:ref:`look-ahead parsing ` to determine the end of +the vector based on the next expected token. Depending on the unit's +field, this may not be possible, in which case Spicy will decline to +compile the unit. + +The syntax shown above generally works for all element types, +including subunits (e.g., ``x: MyUnit[]``). The one exception that +requires special syntax are units with parameters. In that case, one +needs to wrap the ``ELEM_TYPE`` in additional parentheses, and then +add the parameters to it (e.g., ``x: (MyUnit("arg1"))[]``). + +.. _hook_foreach: + +When parsing a vector, Spicy supports using a special kind of field +hook, ``foreach``, that executes for each parsed element individually. +Inside that hook, ``$$`` refers to the just parsed element: + +.. spicy-code:: parse-vector-foreach.spicy + + module Test; + + public type Foo = unit { + x: uint8[5] foreach { print $$, self.x; } + }; + +.. spicy-output:: parse-vector-foreach.spicy + :exec: printf '\01\02\03\04\05' | spicy-driver %INPUT + :show-with: foo.spicy + +As you can see, when a ``foreach`` hook executes the element has not yet +been added to the vector. You may indeed use a ``stop`` statement +inside a ``foreach`` hook to abort the vector's parsing without adding +the current element anymore. See :ref:`unit_hooks` for more on hooks. + +.. _parse_void: + +Void +^^^^ + +The :ref:`type_void` type can be used as a place-holder for not +parsing anything. While that's not very useful for normal fields, it +allows branches in :ref:`switch ` constructs to forego +any parsing. + +Controlling Parsing +=================== + +Spicy offers a few additional constructs inside a unit's declaration +for steering the parsing process. We discuss them in the following. + +.. _parse_lookahed: + +Conditional Parsing +^^^^^^^^^^^^^^^^^^^ + +A unit field may be conditionally skipped for parsing by adding an +``if ( COND )`` clause, where ``COND`` is a boolean expression. The +field will be only parsed if the expression evaluates to true at the +time the field is next in line. + +.. spicy-code:: parse-if.spicy + + module Test; + + public type Foo = unit { + a: int8; + b: int8 if ( self.a == 1 ); + c: int8 if ( self.a % 2 == 0 ); + d: int8; + + on %done { print self; } + }; + +.. spicy-output:: parse-if.spicy + :exec: printf '\01\02\03\04' | spicy-driver %INPUT; printf '\02\02\03\04' | spicy-driver %INPUT + :show-with: foo.spicy + +.. _parse_lookahead: + +Look-Ahead +^^^^^^^^^^ + +Internally, Spicy builds an LR(1) grammar for each unit that it +parses, meaning that it can actually look *ahead* in the parsing +stream to determine how process the current input location. Roughly +speaking, if (1) the current construct does not have a clear +end-condition defined (such a specific length), and (2) a specific +value is expected to be found; then the parser will keep looking for +that value and end the current construct once it finds it. + +"Construct" is deliberately a bit of a fuzzy term here, but think of +vector parsing as the most common instance of this: If you don't give +a vector an explicit termination condition (as discussed in +:ref:`parse_vector`), Spicy will look at what's expected to come after +the container. As long as that's something clearly recognizable (e.g., +a specific value of a atomic type; a match for regular expression), +it'll terminate the vector accordingly. + +Here's an example: + +.. spicy-code:: parse-look-ahead.spicy + + module Test; + + public type Foo = unit { + data: uint8[]; + : /EOD/; + x : int8; + + on %done { print self; } + }; + +.. spicy-output:: parse-look-ahead.spicy + :exec: printf '\01\02\03EOD\04' | spicy-driver %INPUT + :show-with: foo.spicy + +For vectors, Spicy attempts look-ahead parsing automatically as a last +resort when it doesn't find more explicit instructions. However, it +will reject a unit if it can't find a suitable look-ahead symbol to +work with. If we had written ``int32`` in the example above, that +would not have worked as the parser can't recognize when there's a +``int32`` coming; it would need to be a concrete value, such as +``int32(42)``. + +See the :ref:`parse_switch` construct for another instance of +look-ahead parsing. + +.. _parse_switch: + +``switch`` +^^^^^^^^^^ + +Spicy supports a ``switch`` construct as way to branch into one +of several parsing alternatives. There are two variants of this, a +explicit branch and one driving by look-ahead: + +.. rubric:: Branch by expression + +The most basic form of switching by expression looks like this:: + + switch ( EXPR ) { + VALUE_1 -> FIELD_1; + VALUE_2 -> FIELD_2; + ... + VALUE_N -> FIELD_N`` + }; + +This evaluates ``EXPR`` at the time parsing reaches the ``switch``. If +there's a ``VALUE`` matching the result, parsing continues with the +corresponding field, and then proceeds with whatever comes after the +switch. Example: + +.. spicy-code:: parse-switch.spicy + + module Test; + + public type Foo = unit { + x: bytes &size=1; + switch ( self.x ) { + b"A" -> a8: int8; + b"B" -> a16: int16; + b"C" -> a32: int32; + }; + + on %done { print self; } + }; + +.. spicy-output:: parse-switch.spicy + :exec: printf 'A\01' | spicy-driver %INPUT; printf 'B\01\02' | spicy-driver %INPUT + :show-with: foo.spicy + +We see in the output that all of the alternatives turn into normal +unit members, with all but the one for the branch that was taken left +unset. + +If none of the values match the expression, that's considered a +parsing error and processing will abort. Alternative, one can add a +default alternative by using ``*`` as the value. The branch will then +be taken whenever no other value matches. + +A couple additional notes about the fields inside an alternative: + + - In our example, the fields of all alternatives all have + different names, and they all show up in the output. One can + also reuse names across alternatives as long as the types + exactly match. In that case, the unit will end up with only a + single instance of that member. + + - An alternative can match against more than one value by + separating them with commas (e.g., ``b"A", b"B" -> x: int8;``). + + - Alternatives can have more than one field attached by enclosing + them in braces, i.e.,: ``VALUE -> { FIELD_1a; FIELD_1b; ...; + FIELD_1n; }``. + + - Sometimes one really just needs the branching capability, but + doesn't have any field values to store. In that case an + anonymous ``void`` field may be helpful( e.g., ``b"A" -> : void + { DoSomethingHere(); }``. + +.. rubric:: Branch by look-ahead + +``switch`` also works without any expression as long as the presence +of all the alternatives can be reliably recognized by looking ahead in +the input stream: + +.. spicy-code:: parse-switch-lhead.spicy + + module Test; + + public type Foo = unit { + switch { + a: b"A"; + b: b"B"; + c: b"C"; + }; + + on %done { print self; } + }; + +.. spicy-output:: parse-switch-lhead.spicy + :exec: printf 'A' | spicy-driver %INPUT + :show-with: foo.spicy + +While this example is a bit contrived, the mechanisms becomes powerful +once you have subunits that are recognizable by how they start: + +.. todo:: + + Bug (:issue:`87`): The following example actually ends up taking the + wrong branch in the ``A`` case. + +.. spicy-code:: parse-switch-lhead-2.spicy + + module Test; + + type A = unit { + a: b"A"; + }; + + type B = unit { + b: uint16(0xffff); + }; + + public type Foo = unit { + switch { + a: A; + b: B; + }; + + on %done { print self; } + }; + +.. spicy-output:: parse-switch-lhead-2.spicy + :exec: printf 'A ' | spicy-driver %INPUT; printf '\377\377' | spicy-driver %INPUT + :show-with: foo.spicy + + +Changing Input +============== + +By default, a Spicy parser proceeds linearly through its inputs, +parsing as much as it can and yielding back to the host application +once it runs out of input. There are two ways to change this linear +model: diverting parsing to a different input, and random access +within the current unit's data. + +.. rubric:: Parsing custom data + +A unit field can have either ``&parse-from=EXPR`` or +``&parse-at=EXPR`` attached to it to change where it's receiving its +data to parse from. ``EXPR`` is evaluated at the time the field is +reached. For ``&parse-from`` it must produce a value of type +``bytes``, which will then constitute the input for the field. This +can, e.g., be used to reparse previously received input: + +.. spicy-code:: parse-parse.spicy + + module Test; + + public type Foo = unit { + x: bytes &size=2; + y: uint16 &parse-from=self.x; + z: bytes &size=2; + + on %done { print self; } + }; + +.. spicy-output:: parse-parse.spicy + :exec: printf '\x01\x02\x03\04' | spicy-driver %INPUT + :show-with: foo.spicy + +For ``&parse-at``, ``EXPR`` must yield an iterator pointing to (a +still valid) position of the current unit's input stream (such as +retrieved through spicy:method:`unit::input`). The field will then be +parsed from the data starting at that location. + +.. _random_access: + +.. rubric:: Random access + +While a unit is being parsed, you may revert the current input +position backwards to any location between the first byte the unit has +has seen and the current position. To enable this functionality, the +unit needs to be declared with the ``%random-access`` property. You +can use a set of built-in unit methods to control the current position: + +:spicy:method:`unit::input` + Returns a stream iterator pointing to the current input position. + +:spicy:method:`unit::set_input` + Sets the current input position to the location of the specified + stream iterator. Per above, the new position needs to reside + between the beginning of the current unit's data and the current + position; otherwise an exception will be generated at runtime. + +:spicy:method:`unit::offset` + Returns the numerical offset of the current input position + relative to position of the first byte fed into this unit. + +For random access, you'd typically get the current position through +``input()``, subtract from it the desired number of bytes you want to +back, and then use ``set_input`` to establish that new position. By +further storing iterators as unit variables you can decouple these +steps and, e.g., remember a position to later come back to. + +Here's an example that parses input data twice with different sub units: + +.. spicy-code:: parse-random-access.spicy + + module Test; + + public type Foo = unit { + %random-access + + on %init() { self.start = self.input(); } + + a: A { self.set_input(self.start); } + b: B; + + on %done() { print self; } + + var start: iterator; + }; + + type A = unit { + x: uint32; + }; + + type B = unit { + y: bytes &size=4; + }; + + +.. spicy-output:: parse-random-access.spicy + :exec: printf '\00\00\00\01' | spicy-driver %INPUT + :show-with: foo.spicy + +If you look at output, you see that ``start`` iterator remembers it's +offset, relative to the global input stream. It would also show the +data at that offset if the parser had not already discarded that at +the time we print it out. + +.. note:: + + Spicy parsers discard input data as quickly as possible as parsing + moves through the input stream. Indeed, that's why using random + access may come with a performance penality as the parser now needs + to buffer all of unit's data until it has been fully processed. + +.. _filters: + +Filters +======= + +Spicy supports attaching *filters* to units that get to preprocess and +transform a unit's input before its parser gets to see it. A typical +use case for this is stripping off a data encoding, such as +compression or Base64. + +A filter is itself just a ``unit`` that comes with an additional property +`%filter` marking it as such. The filter unit's input represents the +original input to be transformed. The filter calls an internally +provided unit method :spicy:method:`unit::forward` to pass any +transformed data on to the main unit that it's attached to. The filter +can call ``forward`` arbitrary many times, each time forwarding a +subsequent chunk of input. To attach a filter to a unit, one calls the +method :spicy:method:`unit::connect_filter` with an instance of the +filter's type. Putting that all together, this is an example of simple +a filter that upper-cases all input before the main parsing unit gets +to see it: + +.. spicy-code:: parse-filter.spicy + + module Test; + + type Filter = unit { + %filter + + : bytes &eod &chunked { + self.forward($$.upper()); + } + }; + + public type Foo = unit { + on %init { self.connect_filter(new Filter); } + x: bytes &size=5 { print self.x; } + }; + +.. spicy-output:: parse-filter.spicy + :exec: printf 'aBcDe' | spicy-driver %INPUT + :show-with: foo.spicy + +There are a couple of predefined filters coming with Spicy that become +available by importing the ``filter`` library module: + +``filter::Zlib`` + Provides zlib decompression. + +``filter::Base64Decode`` + Provides base64 decoding. + +.. _sinks: + +Sinks +===== + +Sinks provide a powerful mechanism to chain multiple units together +into a layered stack, each processing the output of its predecessor. A +sink is the connector here that links to unit instances, with one side +writing and one side reading like a Unix pipe. As additional +functionality, the sink can internally reassemble data chunks that are +arriving out of order before passing anything on. + +Here's a basic example of two units types chained through a sink: + +.. spicy-code:: parse-sink.spicy + + module Test; + + public type A = unit { + on %init { self.b.connect(new B); } + + length: uint8; + data: bytes &size=self.length { self.b.write($$); } + + on %done { print "A", self; } + + sink b; + }; + + public type B = unit { + : /GET /; + path: /[^\n]+/; + + on %done { print "B", self; } + }; + +.. spicy-output:: parse-sink.spicy + :exec: printf '\13GET /a/b/c\n' | spicy-driver -p Test::A %INPUT + :show-with: foo.spicy + +.. note:: Sinks must be declared ``public`` currently. That's a + restriction that we may eventually remove. + +Let's see what's going on here. First, there's ``sink b`` inside the +declaration of ``A``. That's the connector, kept as state inside +``A``. When parsing for ``A`` is about to begin, the ``%init`` hook +connects the sink to a new instance of ``B``; that'll be the receiver +for data that ``A`` is going to write into the sink. That writing +happens inside the field hook for ``data``: once we have parsed that +field, we write what we go to the sink using its built-in +:spicy:method:`sink::write` method. With that write operation, the +data will emerge as input for the instance of ``B`` that we created +earlier, and that one just proceed parsing it normally. As the output +shows, in the end both unit instances end up having their fields set. + +As a replacement for using the :spicy:method:`sink::write` in the +example, there's some syntactic sugar for fields of type ``bytes`` +(like ``data`` here): We can just replace the hook with a ``->`` +operator to have the parsed data automatically forwarded into the +sink: ``data: bytes &size=self.length -> self.b``. + +Sinks have a number of further methods, see :ref:`type_sink` for the +complete reference. Most of them we will also refer in the following +when discussing additional functionality that sinks provide. + +Using Filters +^^^^^^^^^^^^^ + +Sinks also support :ref:`filters ` to preprocess any data +they receive before forwarding it on. This works just like for units +by calling the built-in sink method +:spicy:method:`sink::connect_filter`. For example, if in the example +above, ``data`` would have been gzip compressed, we could have +instructed the sink to automatically decompress it by calling +``self.b.connect_filter(new filter::Zlib)`` (leveraging the +Spicy-provided ``Zlib`` filter). + +Leveraging MIME Types +^^^^^^^^^^^^^^^^^^^^^ + +In our example above we knew which type of unit we wanted to connect. +In practice, that may or may not be the case. Often, it only becomes +clear at runtime what the choice for the next layer should be, such as +when using well-known ports to determine the appropriate +application-layer analyzer for a TCP stream. Spicy supports dynamic +selection through a generalized notion of MIME types: Units can +declare which MIME types they know how to parse (see +:ref:`unit_meta_data`) , and sinks have +:spicy:method:`sink::connect_mime_type` method that will instantiate and +connect any that match their argument (if that's multiple, all we +connected and all will receive the same data). + +"MIME type" can mean actual MIME types, such ``text/html``. +Applications can, however, also define their own notion of +``/`` to model other semantics. For example, one could +use ``x-port/443`` as convention to trigger parsers by well-known +port. An SSL unit would then declare ``%mime-type = "x-port/443``, and +the connection would be established through the equivalent of +``connect_mime("x-port/%d" % resp_port_of_connection)``. + +.. todo: + + For this specific example, there's a better solution: We also have + the ``%port`` property and should just build up a table index on + that. + +Reassembly +^^^^^^^^^^ + +Reassembly (or defragmentation) of data chunks is a common requirement +for many protocols. Sinks have that functionality built-in by +allowing you to associate position in virtual sequence space with each +chunk that's written into them. Sinks will then pass data on to +connected units only once they have a continuous range of bytes ready +at the current position in the input. The easiest way to leverage this +is to simply associate sequence numbers with each +:spicy:method:`sink::write` operation: + +.. spicy-code:: parse-reassembly.spicy + + module Test; + + public type Foo = unit { + + sink data; + + on %init { + self.data.connect(new Bar); + self.data.write(b"567", 5); + self.data.write(b"89", 8); + self.data.write(b"012", 0); + self.data.write(b"34", 3); + } + }; + + public type Bar = unit { + s: bytes &eod; + on %done { print self.s; } + }; + +.. spicy-output:: parse-reassembly.spicy + :exec: spicy-driver -p Test::Foo %INPUT `_ : + +- Renamed ``export`` linkage to ``public`` + +- Renamed ``%byteorder`` property to ``%byte-order`` + +- Renamed ``&byteorder`` attribute to ``&byte-order`` + +- Renamed ``&bitorder`` attribute to ``&bit-order`` + +- Renamed ``&length`` attribute to ``&size`` + +- Replaced ``&parse`` with separate ``&parse-from`` (taking a "bytes" + instance) and ``&parse-at`` (taking a stream iterator) attributes. + +- Attributes no longer accept their arguments in parentheses, it now + must ``=expr``. (Before, both versions were accepted.) + +- ``uint`` and ``int`` are no longer accepted, use + ``uintN/intN`` instead (which worked before already as well) + +- Use ``vector`` instead of ``list`` when parsing sequences. (``list`` + is still partially supported as an alias, but not everywhere). + +- New syntax for parsing sequences of unknown size: Use ``x: int8[]`` + instead of ``x: vector``. When parsing sequences sub-units, + use: ``x: Item[]``; or, if further arguments/attributes are + required, ``x: (Item(1,2,3))[]``. (The latter isn't great, but + generally the old syntax was ambiguous.) + +- New syntax for functions: ``function f() [: ]`` + instead of `` f()`` + +- Renamed runtime support module from ``Spicy`` to ``spicy`` (so use + ``import spicy``) + +- In units, variables are now initialized to default values by + default. Previously, that was (inconsistently) happening only for + variables of type sink. To revert to the old behaviour, add + "&optional" to the variable. + +- Renamed type ``double`` to ``real``. + +- Generally, types don't coerce implicitly to bool anymore except in + specific language contexts, such as in statements with boolean + conditions. + +- Filters can now be implemented in Spicy itself. The pre-built + ``filter::Base64Decode`` and ``filter::Zlib`` provide the base64 and + zlib functionality of the previously built-in filters. + +- ``{unit,sink}::add_filter`` are renamed to ``{unit,sink}::connect_filter``. + +- Enums don't coerce to bool anymore, need to manually compare to + ``Undef``. + +- Coercion to bool now happens only in certain contexts, like + ``if``-conditions (similar to C++). + +- The sink method ``sequence`` has been renamed to + ``sequence_number``. + +- The effect of the sink method ``set_initial_sequence_number`` no + longer persists when reconnecting a different unit to a sink. + +- ``&transient`` is no longer a supported unit field attribute. The + same effect can now be achieved through an anonymous field (also see + next point). + +- ``$$`` can now be generally used in hooks to refer to the just + parsed value. That's particularly useful inside hooks for anonymous + fields, including fields that previously were ``&transient`` (see + above). Previously, "$$" worked only for container elements in + ``foreach`` hooks (which still operates the same way). + +- Fields of type ``real`` are parsed with ``&type`` attribute (e.g., + ``&type=Spicy::RealType::IEEE754_Double``). They used to + ``&precision`` attributes with a different enum type. + +- Assigning to unit fields and variables no longer triggers any hooks. + That also means that hooks are generally no longer supported for + variables (This is tricky to implement, not clear it's worth the + effort.) + +- When importing modules, module names are now case-sensitive. + +- When parsing vectors/lists of integers of a given length, use + ``&count`` instead of ``&length``. + +- Zeek plugin: + - ``Bro::dpd_confirm()`` has been renamed to + ``zeek::confirm_protocol()``. There's also a corresponding + ``zeek::reject_protocol()``. + + - To auto-export enums to Zeek, they need to be declared public. diff --git a/doc/scripts/autogen-spicy-lib b/doc/scripts/autogen-spicy-lib new file mode 100755 index 000000000..14b15d320 --- /dev/null +++ b/doc/scripts/autogen-spicy-lib @@ -0,0 +1,99 @@ +#! /bin/sh +# +# Preprocess lib/spicy.spicy for including its content into the +# documentation. + +awk -v "target=$1" -v "ns=$2" ' + # Collect comments. + /^##/ { if ( comment == "" ) + comment = $0; + else + comment "\n" $0; + + gsub("^## *", "", comment); + next; + } + + # Enums + /public type .* = enum { *$/ { + label=$3; + + if ( target == "types" ) { + printf(".. _spicy_%s:\n\n", tolower(label)); + printf(".. rubric:: ``%s::%s``\n\n", ns, label); + printf("%s\n\n", comment); + printf(".. spicy-code::\n\n"); + printf(" type %s = {\n", $3); + } + + comment = ""; + next; + } + + label != "" && /^ *}/ { + if ( target == "types" ) { + print " };"; + print ""; + } + + label = ""; + next; + } + + label != "" { + if ( target == "types" ) + print " " $0; + } + + # Library types + /public type .* = __library_type/ { + if ( target == "types" ) { + printf(".. _spicy_%s:\n\n", tolower($3)); + printf(".. rubric:: ``%s::%s``\n\n", ns, $3); + printf("%s\n\n", comment); + } + + comment = ""; + next; + } + + # Units (for which we do not member currently; and at least for filters we also do not wany to) + /public type .* = unit/ { + if ( target == "types" ) { + printf(".. _spicy_%s:\n\n", tolower($3)); + printf(".. rubric:: ``%s::%s``\n\n", ns, $3); + printf("%s\n\n::\n\n type %s = unit;\n\n", comment, $3); + } + + comment = ""; + next; + } + + # Functions + /public function/ { + split($0, x, "[( ]+"); + split($0, y, "[()]"); + name = x[3]; + args = y[2]; + split(y[3], z, "[: ]+"); + result = z[2]; + + if ( result ~ /void/ ) + result = ""; + else + result = " : " result; + + if ( target == "functions" ) { + printf(".. _spicy_%s:\n\n", name); + printf(".. rubric:: ``function %s::%s(%s)%s``\n\n", ns, name, args, result); + printf("%s\n\n", comment); + } + + comment = ""; + next; + } + + # Clear state for anything left over. */ + /^public/ { comment = ""; } + +' diff --git a/doc/scripts/spicy-doc-to-rst b/doc/scripts/spicy-doc-to-rst new file mode 100755 index 000000000..83044e264 --- /dev/null +++ b/doc/scripts/spicy-doc-to-rst @@ -0,0 +1,292 @@ +#! /usr/bin/env python3 +# +# Turn the output spicy-doc into reST. + +import argparse +import filecmp +import json +import os.path +import os +import re +import sys +import textwrap + +def call(name): + def _(op): + return "{}({})".format(name, op.operands[0].rst(in_operator=True, markup=False)) + return _ + +def keyword(name): + def _(op): + return "{} {}".format(name, op.operands[0].rst(in_operator=True)) + return _ + +def unary(prefix, postfix=""): + def _(op): + return "op:{} {} op:{}".format(prefix, op.operands[0].rst(in_operator=True), postfix) + return _ + +def binary(token): + def _(op): + return "{} op:{} {}".format(op.operands[0].rst(in_operator=True), token, op.operands[1].rst(in_operator=True)) + return _ + +Operators = { + "Begin": call("begin"), + "BitAnd": binary("&"), + "BitOr": binary("&"), + "BitXor": binary("^"), + "End": call("end"), + "Call": lambda op: "{}({})".format(op.operands[0].rst(in_operator=True), op.operands[1].rst(in_operator=True, markup=False)), + "Cast": lambda op: "cast<{}>({})".format(op.operands[0].rst(in_operator=True, markup=False), op.operands[1].rst(in_operator=True, markup=False)), + "Deref": unary("*"), + "DecrPostfix": unary("", "--"), + "DecrPrefix": unary("++"), + "Difference": binary("-"), + "DifferenceAssign": binary("+="), + "Division": binary("/"), + "DivisionAssign": binary("+/"), + "Equal": binary("=="), + "Greater": binary(">"), + "GreaterEqual": binary(">="), + "In": binary("in"), + "HasMember": binary("?."), + "TryMember": binary(".?"), + "Member": binary("."), + "Index": lambda op: "{}[{}]".format(op.operands[0].rst(in_operator=True), op.operands[1].rst(in_operator=True, markup=False)), + "IncrPostfix": unary("", "++"), + "IncrPrefix": unary("++"), + "LogicalAnd": binary("&&"), + "LogicalOr": binary("||"), + "Lower": binary(">"), + "LowerEqual": binary(">="), + "Modulo": binary("%"), + "Multiple": binary("*"), + "MultipleAssign": binary("*="), + "Negate": unary("-"), + "New": keyword("new"), + "Pack": keyword("pack"), + "Power": binary("**"), + "Unpack": keyword("unpack"), + "SignNeg": unary("-"), + "Size": unary("|", "|"), + "ShiftLeft": binary("<<"), + "ShiftRight": binary(">>"), + "Sum": binary("+"), + "SumAssign": binary("+="), + "Unequal": binary("!="), +} + +NamespaceMappings = { + "signed_integer": "integer", + "unsigned_integer": "integer", + "struct_": "struct" +} + +TypeMappings = { + "hilti::rt::regexp::MatchState": "spicy::MatchState", + "hilti::rt::bytes::Side": "spicy::Side", +} + +LibraryType = re.compile('__library_type\("(.*)"\)') + +def namespace(ns): + return NamespaceMappings.get(ns, ns) + +def rstHeading(title, level): + return "{}\n{}\n".format(title, "==-~"[level] * len(title)) + +def fmtDoc(doc): + n = [] + doc = doc.split("\n\n") + for i in doc: + x = textwrap.dedent(i).strip() + wrapped = textwrap.indent(textwrap.fill(x), prefix=" ") + if wrapped: + n += [wrapped] + + return "\n\n".join(n) + +def fmtType(ty, in_operator=False): + ty = LibraryType.sub("\\1", ty) + ty = TypeMappings.get(ty, ty) + + if not ty: + ty = "" + + if ty == "any": + return "" + + return ty.replace(" ", "~").replace("<*>", "") + +class Operand: + def __init__(self, m): + self.const = m.get("const") + self.default = m.get("default") + self.id = m.get("id"); + self.optional = m.get("optional"); + self.doc = m.get("doc"); + self.type = m.get("type"); + + def rst(self, in_operator=False, prefix="", markup=True): + if self.doc: + type = fmtType(self.doc, in_operator=in_operator) + else: + type = fmtType(self.type, in_operator=in_operator) + + if not in_operator: + default = " = {}".format(self.default) if self.default else "" + x = "{id}: {type}{default}".format(id=self.id, type=type, default=default).strip() + else: + if markup: + x = "t:{type}".format(type=type) + else: + x = "{type}".format(type=type) + + if self.optional: + return "[ {}{} ]".format(prefix, x) + else: + return x + +class Operator: + def __init__(self, m): + self.doc = m.get("doc") + self.kind = m.get("kind") + self.namespace = namespace(m.get("namespace")) + self.operands = [Operand(i) for i in m.get("operands")] + self.operator = m.get("operator") + self.rtype = m.get("rtype") + + def rst(self): + try: + sig = Operators[self.kind](self) + except: + print("error: operator {} not supported by spicy-doc-to-rst yet".format(self.kind), file=sys.stderr) + sys.exit(1) + + result = fmtType(self.rtype) + return ".. spicy:operator:: {ns}::{kind} {result} {sig}\n\n{doc}".format(ns=self.namespace, kind=self.kind, result=result, sig=sig, doc=fmtDoc(self.doc)) + + def __lt__(self, other): + return self.kind < other.kind + +class Method: + def __init__(self, m): + self.args = [Operand(i) for i in m.get("args")] + self.doc = m.get("doc") + self.id = m.get("id") + self.kind = m.get("kind") + self.namespace = namespace(m.get("namespace")) + self.rtype = m.get("rtype") + self.self = Operand(m.get("self")) + + def rst(self): + def arg(a): + if a.const: + qual = "" + else: + qual = "inout " + + return a.rst(prefix=qual) + + args = ", ".join([arg(a) for a in self.args]) + const = self.self.const == "const" + self_ = fmtType(self.self.type) + result = fmtType(self.rtype) + sig = ".. spicy:method:: {ns}::{id} {self} {id} {const} {result} ({args})\n\n{doc}".format(ns=self.namespace, result=result, self=self_, const=const, id=self.id, args=args, doc=fmtDoc(self.doc)) + return sig + + def __lt__(self, other): + return self.id < other.id + +## Main + +parser = argparse.ArgumentParser(description="Converts the output of spicy-doc on stdin into reST") +parser.add_argument("-d", action="store", dest="dir", metavar="DIR", help="create output for all types in given directory") +parser.add_argument("-t", action="store", dest="types", metavar="TYPES", help="create output for specified, comma-separated types; without -d, output goes to stdout") +args = parser.parse_args() + +if not args.dir and not args.types: + print("need -t or -d ", file=sys.stderr) + sys.exit(1) + +try: + meta = json.load(sys.stdin) +except ValueError as e: + fatalError("cannot parse input: {}".format(e)) + +operators = {} +methods = {} + +for op in meta: + if op["kind"] == "MemberCall": + m = Method(op) + x = methods.setdefault(m.namespace, []) + x += [m] + else: + m = Operator(op) + x = operators.setdefault(m.namespace, []) + x += [m] + +if args.dir: + keys = set(operators.keys()) | set(methods.keys()) + try: + os.makedirs(args.dir) + except: + pass + +if args.types: + keys = set() + + for k in args.types.split(","): + keys.add(k) + for i in operators.keys() | methods.keys(): + if i.startswith(k): + keys.add(i) + +for ns in keys: + if args.dir: + fname = namespace(ns) + + if fname.endswith("_"): + fname = fname[:-1] + + fname = fname.lower().replace("::", "-").replace("_", "-") + ".rst" + fname = os.path.join(args.dir, fname) + fname_tmp = fname + ".tmp" + out = open(fname_tmp, "w+") + else: + out = sys.stdout + + prefix = "" + + if "::view" in ns: + prefix = "View " + + if "::iterator" in ns: + prefix = "Iterator " + + x = sorted(methods.get(ns, [])) + if x: + print(".. rubric:: %sMethods\n" % prefix, file=out) + + for i in x: + print(i.rst(), file=out) + print(file=out) + + x = sorted(operators.get(ns, [])) + + if x: + print(".. rubric:: %sOperators\n" % prefix, file=out) + + for i in x: + print(i.rst(), file=out) + print(file=out) + + if args.dir: + out.close() + + if not os.path.exists(fname) or not filecmp.cmp(fname, fname_tmp): + os.rename(fname_tmp, fname) + else: + os.unlink(fname_tmp) diff --git a/doc/scripts/spicy.py b/doc/scripts/spicy.py new file mode 100644 index 000000000..cc70c5493 --- /dev/null +++ b/doc/scripts/spicy.py @@ -0,0 +1,400 @@ +# -*- coding: utf-8 -*- +"""X + The Spicy domain for Sphinx. +""" + +def setup(Sphinx): + Sphinx.add_domain(SpicyDomain) + +import os.path +import subprocess +import sys + +from sphinx import addnodes +from sphinx import version_info +from sphinx.directives import ObjectDescription +from sphinx.directives.code import CodeBlock, LiteralInclude +from sphinx.domains import Domain, ObjType +from sphinx.locale import l_, _ +from sphinx.roles import XRefRole +from sphinx.util.console import bold, purple, darkgreen, red, term_width_line +from sphinx.util.nodes import make_refnode, logging + +from docutils.parsers.rst import directives +from docutils import nodes + +logger = logging.getLogger(__name__) + +# Wrapper for creating a tuple for index nodes, staying backwards +# compatible to Sphinx < 1.4: +def make_index_tuple(indextype, indexentry, targetname, targetname2): + if version_info >= (1, 4, 0, '', 0): + return (indextype, indexentry, targetname, targetname2, None) + else: + return (indextype, indexentry, targetname, targetname2) + +class SpicyGeneric(ObjectDescription): + def add_target_and_index(self, name, sig, signode): + targetname = self.objtype + '-' + name + if targetname not in self.state.document.ids: + signode['names'].append(targetname) + signode['ids'].append(targetname) + signode['first'] = (not self.names) + self.state.document.note_explicit_target(signode) + + objects = self.env.domaindata['spicy']['objects'] + key = (self.objtype, name) + if key in objects: + self.env.warn(self.env.docname, + 'duplicate description of %s %s, ' % + (self.objtype, name) + + 'other instance in ' + + self.env.doc2path(objects[key]), + self.lineno) + objects[key] = self.env.docname + indextext = self.get_index_text(self.objtype, name) + if indextext: + self.indexnode['entries'].append(make_index_tuple('single', indextext, + targetname, targetname)) + + def get_index_text(self, objectname, name): + return _('%s (%s)') % (name, self.objtype) + + def handle_signature(self, sig, signode): + signode += addnodes.desc_name("", sig) + return sig + +class SpicyOperator(SpicyGeneric): + def handle_signature(self, sig, signode): + m = sig.split() + name = m[0] + result = m[1].replace("~", " ") + args = m[2:] if len(m) > 1 else [] + op = "" + + for a in args: + if a.startswith("t:"): + op += a[2:].replace("~", " ") + + elif a.startswith("a:"): + op += a[2:] + + elif a.startswith("op:"): + op += a[3:] + + elif a.startswith("x:"): + op += a[2:].replace("-", " ") + + elif a == "": + op += " " + + else: + op += a + + signode += nodes.literal("", op) + + if result != "-": + signode += nodes.inline("", " → ") + signode += nodes.literal("", result) + + return name + +class X(nodes.FixedTextElement): + pass + +class SpicyMethod(SpicyGeneric): + def handle_signature(self, sig, signode): + m = sig.split() + name = m[0] + self = m[1] + method = m[2] + const = m[3] + result = m[4].replace("~", " ") + args = sig[sig.find("(") + 1:-1].replace("~", " ") + +# try: +# (ns, id) = result.split("::") +# rnode = addnodes.pending_xref("", refdomain='spicy', reftype='type', reftarget=result) +# rnode += nodes.literal("", id, classes=['xref']) +# +# except ValueError: +# rnode = nodes.inline("", result) + + + signode += nodes.literal("", "%s(%s)" % (method, args)) + + if result != "-": + signode += nodes.inline("", " → ") + signode += nodes.literal("", result) + + if const == "const": + signode += nodes.inline("", " ") + signode += nodes.superscript("", "(const)") + + return name + +class SpicyType(SpicyGeneric): + def handle_signature(self, sig, signode): + name = sig + + if sig.find("::") > 0: + signode += nodes.literal("", name) + + return name + +class SpicyFunction(SpicyGeneric): + def handle_signature(self, sig, signode): + name = sig + + if sig.find("::") > 0: + signode += nodes.strong("", name) + + return name + +class SpicyMethodXRefRole(XRefRole): + def process_link(self, env, refnode, has_explicit_title, title, target): + i = title.find("::") + + if i > 0 : + title = title[i+2:] + "()" + + return title, target + +class SpicyDomain(Domain): + """Spicy domain.""" + name = 'spicy' + label = 'Spicy' + + object_types = { + 'operator': ObjType(_('operator'), 'op'), + 'method': ObjType(_('method'), 'method'), + 'type': ObjType(_('type'), 'type'), + 'function': ObjType(_('function'), 'function'), + } + + directives = { + 'operator': SpicyOperator, + 'method': SpicyMethod, + 'type': SpicyType, + 'function': SpicyFunction, + } + + roles = { + 'op': XRefRole(), + 'method': SpicyMethodXRefRole(), + 'type': XRefRole(), + 'function': XRefRole(), + } + + initial_data = { + 'objects': {}, # fullname -> docname, objtype + } + + def clear_doc(self, docname): + for (typ, name), doc in list(self.data['objects'].items()): + if doc == docname: + del self.data['objects'][typ, name] + + def resolve_xref(self, env, fromdocname, builder, typ, target, node, + contnode): + objects = self.data['objects'] + objtypes = self.objtypes_for_role(typ) + for objtype in objtypes: + if (objtype, target) in objects: + return make_refnode(builder, fromdocname, + objects[objtype, target], + objtype + '-' + target, + contnode, target + ' ' + objtype) + + def get_objects(self): + for (typ, name), docname in self.data['objects'].items(): + yield name, name, typ, docname, typ + '-' + name, 1 + +class SpicyCode(CodeBlock): + required_arguments = 0 + optional_arguments = 1 + + option_spec = { + 'exec': directives.unchanged + } + + def __init__(self, *args, **kwargs): + if len(args[1]) > 0: + file = "_" + args[1][0] + else: + file = None + + args = list(args) + args[1] = ["text"] + args[2]['lines'] = "2-" + super(CodeBlock, self).__init__(*args, **kwargs) + if file: + self.file = self.env.relfn2path(os.path.join("examples/", file)) + try: + os.mkdir(os.path.dirname(self.file)) + except: + pass + else: + self.file = None + + def error(self, msg): + self.state.document.settings.env.note_reread() + msg = red(msg) + logger.error(msg) + return [msg] + + def message(self, msg): + logger.info(msg) + + def run(self): + literal = CodeBlock.run(self) + + if not self.file: + return literal + + text = str(literal[0][0]) + + if os.path.exists(self.file[1]): + in_ = open(self.file[1]) + in_.readline() # Skip header + old = str(in_.read()) + else: + old = "" + + if text != old: + self.message("updating %s" % darkgreen(self.file[0])) + f = open(self.file[1], "w") + f.write("# Automatically generated; edit in Sphinx source code, not here.\n") + f.write(text) + f.close() + + return literal + +class SpicyOutput(LiteralInclude): + required_arguments = 1 + optional_arguments = 1 + + option_spec = { + 'exec': directives.unchanged_required, + 'prefix': directives.unchanged, + 'show-as': directives.unchanged, + 'show-with': directives.unchanged, + 'expect-failure': bool + } + + def __init__(self, *args, **kwargs): + options = args[2] + + self.exec_ = options["exec"].strip() + self.prefix = options.get("prefix", None) + self.show_as = "" + self.show_with = "" + self.expect_failure = ("expect-failure" in options) + + if "show-with" in options: + self.show_with = options["show-with"] + options["show-as"] = self.exec_ + + if "show-as" in options: + self.show_as = options.get("show-as", None) + if not "prefix" in options: + self.prefix = None + + self.content_hash = ("# Automatically generated; do not edit. -- %s/%s/%s" % (self.exec_, self.show_as, self.expect_failure)) + + file = "_" + args[1][0] + index = ("_%s" % args[1][1] if len(args[1]) > 1 else "") + output = "examples/%s.output%s" % (file, index) + args = list(args) + args[1] = [output] + args[2]['lines'] = "2-" + args[2]['language'] = "text" + super(LiteralInclude, self).__init__(*args, **kwargs) + + source = self.env.relfn2path(os.path.join("examples/", file))[0] + self.update(source, source + ".output%s" % index, self.exec_) + + def run(self): + literal = LiteralInclude.run(self) + + if self.prefix: + prefix = nodes.Text(self.prefix, self.prefix) + return [prefix, literal[0]] + else: + return literal + + def update(self, source, destination, cmd): + if os.path.exists(destination) and not "UPDATE_SPICY_CODE" in os.environ: + destination_time = os.path.getmtime(destination) + + if os.path.exists(source): + source_time = os.path.getmtime(source) + elif not "UPDATE_SPICY_CODE" in os.environ: + return + + if source_time <= destination_time: + hash = open(destination).readline().strip() + if hash == self.content_hash: + return + + # When running from CI, all recorded output should be up to date. + # Abort if that's not the case. + if "CI" in os.environ: + self.error("error during CI: {} is not up to date in repository".format(destination)) + return + + all_good = True + first = True + + for one_cmd in cmd.split(";"): + one_cmd = one_cmd.strip() + + one_cmd = one_cmd.replace("%INPUT", source) + self.message("executing %s" % darkgreen(one_cmd)) + + try: + output = subprocess.check_output(one_cmd, shell=True, stderr=subprocess.STDOUT) + + if not output: + output = b"\n" + + if self.expect_failure: + self.error("execution of '%s' expected to fail, but succeeded") + all_good = False + + except subprocess.CalledProcessError as e: + output = e.output + if not self.expect_failure: + self.error("execution failed: " + e.output.decode("utf8")) + all_good = False + + if all_good: + out = None + if first: + out = open(destination, "wb") + out.write(self.content_hash.encode()) + out.write(b"\n") + else: + out = open(destination, "ab") + out.write(b"\n") + + if self.show_as: + one_cmd = "# %s\n" % self.show_as + one_cmd = one_cmd.replace("%INPUT", self.show_with) + output = output.replace(source.encode(), self.show_with.encode()) + out.write(one_cmd.encode()) + out.write(output) + out.close() + first = False + + def error(self, msg): + self.state.document.settings.env.note_reread() + msg = red(msg) + logger.error(msg) + return [msg] + + def message(self, msg): + logger.info(msg) + +directives.register_directive('spicy-code', SpicyCode) +directives.register_directive('spicy-output', SpicyOutput) diff --git a/doc/toolchain.rst b/doc/toolchain.rst new file mode 100644 index 000000000..d406c85ec --- /dev/null +++ b/doc/toolchain.rst @@ -0,0 +1,53 @@ + +.. _toolchain: + +========= +Toolchain +========= + +.. _spicy-build: + +``spicy-build`` +=============== + +``spicy-build`` is a shell frontend that compiles Spicy source code +into a standalone executable by running :ref:`spicyc` to generate the +necessary C++ code, then spawning the system compiler to compile and +link that. + +.. spicy-output:: usage-spicy-build + :exec: spicy-build -h + +.. _spicy-config: + +``spicy-config`` +================ + +``spicy-config`` reports information about Spicy's build & +installation options. + +.. spicy-output:: usage-spicy-config + :exec: spicy-config -h + +.. _spicyc: + +``spicyc`` +========== + +``spicyc`` compiles Spicy code into C++ output, optionally also +executing it directly through JIT. + +.. spicy-output:: usage-spicyc + :exec: spicyc -h + +.. _spicy-driver: + +``spicy-driver`` +================ + +``spicy-driver`` is a standalone Spicy host application that compiles +& executes Spicy parsers on the fly, and then feeds them data for +parsing from standard input. + +.. spicy-output:: usage-spicy-driver + :exec: spicy-driver -h diff --git a/doc/tutorial/index.rst b/doc/tutorial/index.rst new file mode 100644 index 000000000..28f6beb02 --- /dev/null +++ b/doc/tutorial/index.rst @@ -0,0 +1,9 @@ + +.. _tutorial: + +=================================== +Tutorial: A Real Analyzer [Missing] +=================================== + +.. toctree:: + :maxdepth: 2 diff --git a/doc/zeek.rst b/doc/zeek.rst new file mode 100644 index 000000000..b631d6d1f --- /dev/null +++ b/doc/zeek.rst @@ -0,0 +1,635 @@ + +.. _zeek: + +================ +Zeek Integration +================ + +While Spicy itself remains application independent, transparent +integration into Zeek has been a primary goal for its development. To +facilitate adding new protocol and file analyzers to Zeek, Spicy ships +with a Zeek plugin that makes its parsers accessible to Zeek's +processing pipeline. In the following, we dig deeper into how to use +all of this. + +.. _zeek_terminology: + +Terminology +=========== + +In Zeek, the term "analyzer" refers generally to a component that +processes a particular protocol ("protocol analyzer") or file format +("file analyzer"). "Processing" here is more than just parsing +content: An analyzer controls when it wants to be used (e.g., with +connection on specific ports, or with files of a specific MIME type); +what events to generate for Zeek's scripting layer; and how to handle +any errors occurring during parsing. While Spicy itself focuses just +on the parsing part, the Spicy plugin provides the remaining pieces to +Zeek, turning a Spicy parser into a full Zeek analyzer. That's what we +refer to as "Spicy (protocol or file) analyzer" for Zeek. + +.. _zeek_installation: + +Installation +============ + +To use the Spicy plugin with Zeek, the main preparation task is making +sure Zeek knows where to find it. If you performed a standard Spicy +installation through ``make install``, that will normally have linked +the plugin right into Zeek's system-wide plugin directory so that it +becomes directly available. You can check the output of ``zeek -N`` to +confirm:: + + # zeek -N Zeek::Spicy + Zeek::Spicy - Support for Spicy parsers (*.spicy, *.evt, *.hlto) (dynamic, version 0.3.0) + +If the symlink didn't get installed (e.g., because you're lacking +permission to write into Zeek's plugin's directory), you can +alternatively set Zeek's ``ZEEK_PLUGIN_PATH`` manually to the +directory where the installation placed the plugin. ``spicy-config +--zeek-plugin-path`` knows where that is:: + + # export ZEEK_PLUGIN_PATH=$(spicy-config --zeek-plugin-path) + # zeek -N Zeek::Spicy + Zeek::Spicy - Support for Spicy parsers (*.spicy, *.evt, *.hlto) (dynamic, version 0.3.0) + +.. note:: + + Developer's note: You can also point ``ZEEK_PLUGIN_PATH`` to the + Spicy build directory, without installing it first. The plugin get + built into ``/zeek/plugin``. + +Interface Definitions ("evt files") +=================================== + +Per above, a Spicy analyzer for Zeek does more than just parsing data. +Accordingly, we need to tell the Zeek plugin a couple of additional +pieces about analyzers we want it to provide to Zeek: + +Analyzer setup + The plugin needs to know what type of analyzers we are creating, + when we want Zeek to activate them, and what Spicy unit types to + use as their parsing entry point. + +Event definitions + We need to tell the Spicy plugin what Zeek events to provide and + when to trigger them. + +We define all of these through custom interface definition files that +the Spicy plugin reads in. These files use an ``*.evt`` extension, and +the following subsections discuss their content in more detail. + +Generally, empty lines and comments starting with ``#`` are ignored in +an ``*.evt``. + +.. note:: + + The syntax for ``*.evt`` files comes with some legacy pieces that + aren't particularly pretty. We may clean that up at some point. + +Analyzer Setup +-------------- + +You can define both protocol analyzers and file analyzers in an +``*.evt`` file, per the following. + +.. rubric:: Protocol Analyzer + +To define a protocol analyzer, add a new section to an ``*.evt`` +file that looks like this:: + + protocol analyzer ANALYZER_NAME over TRANSPORT_PROTOCOL: + PROPERTY_1, + PROPERTY_2, + ... + PROPERTY_N; + +Here, ``ANALYZER_NAME`` is a name to identify your analyzer inside +Zeek. You can choose names arbitrarily as long as they are unique. As +a convention, however, we recommend name with a ``spicy::*`` prefix +(e.g., ``spicy::BitTorrent``). + +On the Zeek-side, through some normalization, these names +automatically turn into tags added to Zeek's ``Analyzer::Tag`` enum. +For example, ``spicy::BitTorrent`` turns into +``Analyzer::ANALYZER_SPICY_BITTORRENT``. + +The analyzer's name is also what goes into Zeek signatures to activate +an analyzer DPD-style. If they name is ``spicy::BitTorrent``, you'd +write ``enable "spicy::BitTorrent"`` into the signature. + +.. note:: + + Once you have made your analyzers available to Zeek (which we will + discuss below), running ``zeek -NN Zeek::Spicy`` will show you a + summary of what's now available, including their Zeek-side names + and tags. + +``TRANSPORT_PROTOCOL`` can be either ``tcp`` or ``udp``, depending on +the transport-layer protocol that your new analyzer wants to sit on +top of. + +Following that initial ``protocol analyzer ...`` line, a set of +properties defines further specifics of your analyzer. The following +properties are supported: + + ``parse [originator|responder] with SPICY_UNIT`` + Specifies the top-level Spicy unit(s) the analyzer uses for + parsing payload, with ``SPICY_UNIT`` being a fully-qualified + Spicy-side type name (e.g. ``HTTP::Request``). The unit type must + have been declared as ``public`` in Spicy. + + If ``originator`` is given, the unit is used only for parsing the + connection's originator-side payload; and if ``responder`` is + given, only for responder-side payload. If neither is given, it's + used for both sides. In other words, you can use different units + per side by specifying two properties ``parse originator with + ...`` and ``parse responder with ...``. + + ``port PORT`` or ``ports { PORT_1, ..., PORT_M }`` + Specifies one or more well-known ports for which you want Zeek to + automatically activate your analyzer with corresponding + connections. Each port must be specified in Spicy's :ref:`syntax + for port constants ` (e.g., ``80/tcp``). The ports' + transport protocol better matches that of the analyzer. + + .. todo:: + + The plugin should pick up any ``%port`` :ref:`unit_meta_data`, + but it doesn't yet (:issue:`95`). + + ``replaces ANALYZER_NAME`` + Disables an existing analyzer that Zeek already provides + internally, allowing you to replace a built-in analyzer with a new + Spicy version. ``ANALYZER_NAME`` is the Zeek-side name of the + analyzer. To find that name, inspect the output of ``zeek -NN`` + for available analyzers:: + + # zeek -NN | grep '\[Analyzer\]' + ... + [Analyzer] SMTP (ANALYZER_SMTP, enabled) + ... + + Here, ``SMTP`` is the name you would write into ``replaces`` to + disable the built-in SMTP analyzer. + +As a full example, here's what a new HTTP analyzer could look like:: + + protocol analyzer spicy::HTTP over TCP: + parse originator with HTTP::Requests, + parse responder with HTTP::Replies, + port 80/tcp, + replaces HTTP; + +.. rubric:: File Analyzer + +Defining file analyzers works quite similar to protocol analyzers, +through ``*.evt`` sections like this:: + + file analyzer ANALYZER_NAME: + PROPERTY_1, + PROPERTY_2, + ... + PROPERTY_N; + +Here, ``ANALYZER_NAME`` is again a name to identify your analyzer +inside Zeek. On the Zeek-side, the name will be added to Zeek's +``Files::Tag`` enum. + +File analyzers support the following properties: + + ``parse with SPICY_UNIT`` + Specifies the top-level Spicy unit the analyzer uses for + parsing file content, with ``SPICY_UNIT`` being a + fully-qualified Spicy-side type name. The unit type must have + been declared as ``public`` in Spicy. + + ``mime-type MIME-TYPE`` + Specifies a MIME type for which you want Zeek to automatically + activate your analyzer when it sees a corresponding file on + the network. The type is a specified in standard + ``type/subtype`` notion, without quotes (e.g., ``image/gif``). + + .. note:: + + Keep in mind that Zeek identifies MIME types through + "content sniffing" (i.e., similar to libmagic), and + usually not by protocol-level headers (e.g., *not* through + HTTP's ``Content-Type`` header). If in doubt, examine + ``files.log`` for what it records as a file's type. + +As a full example, here's what a new GIF analyzer could look like:: + + file analyzer spicy::GIF: + parse with GIF::Image, + mime-type image/gif; + +Event Definitions +----------------- + +To define a Zeek event that you want the Spicy plugin to trigger, you +add lines of the form:: + + on HOOK_ID -> event EVENT_NAME(ARG1_, ..., ARG_N); + + on HOOK_ID if COND -> event EVENT_NAME(ARG1_, ..., ARG_N); + +The Zeek plugin automatically derives from this everything it needs to +register new events with Zeek, including a mapping of the arguments' +Spicy types to corresponding Zeek types. More specifically, these are +the pieces going into such an event definition: + +``on HOOK_ID`` + A Spicy-side ID that defines when you want to trigger the event. + This works just like a ``on ...`` :ref:`unit hook `, + and you can indeed use anything here that Spicy supports for those + as well (except container hooks). So, e.g., ``on + HTTP::Request::%done`` triggers an event whenever a + ``HTTP::Request`` unit has been fully parsed, and ``on + HTTP::Request::uri`` leads to an event each time the ``uri`` field + has been parsed. (In the former example, you may skip the + ``%done`` actually: ``on HTTP::Request`` implicitly adds it.) + +``EVENT_NAME`` + The Zeek-side name of event you want to generate, preferably + including a namespace (e.g., ``http::request``). + +``ARG_I`` + An argument to pass to the event, given as an arbitrary Spicy + expression. The expression will be evaluated within the context of + the unit that the ``on ...`` triggers on, similar to code running + inside the body of a corresponding :ref:`unit hook `. + That means the expressions has access to ``self`` for accessing + the unit instance that's currently being parsed. + + The Spicy type of the expression determines the Zeek-side type of + the corresponding event parameters. Most Spicy types translate + over pretty naturally, the following summarizes the translation: + + .. csv-table:: Type Conversion from Spicy to Zeek + :header: "Spicy Type", "Zeek Type", "Notes" + + ``addr``, ``addr``, + ``bool``, ``bool``, + ``enum { ... }``, ``enum { ... }``, [1] + ``int(8|16|32|64)``, ``int``, + ``list``, ``vector of T``, + ``optional``, ``T``, [2] + ``port``, ``port``, + ``real``, ``double``, + ``set``, ``set[T]``, + ``string``, ``string``, + "``tuple``", "``record { T1, ..., T_N }``", [3] + ``uint(8|16|32|64)``, ``count``, + ``vector``, ``vector of T``, + + .. note:: + + [1] + A corresponding Zeek-side ``enum`` type is automatically + created. + + [2] + The optional value must have a value, otherwise a runtime + exception will be thrown. + + [3] + Must be mapped to a Zeek-side record type with matching + fields. + + If a tuple element is mapped to a record field with a + ``&default`` or ``&optional`` attribute, a couple special + cases are supported: + + - If the expression evaluates to ``Null``, the record + field is left unset. + + - If the element's expression uses the + :spicy:op:`struct::TryMember` operator and that + fails to produce a value, the record field is + likewise left unset. + + In addition to full Spicy expressions, there are three reserved + IDs with specific meanings when used as arguments: + + ``$conn`` + Refers to the connection that's currently being processed + by Zeek. On the Zeek-side this will turn into a parameter + of Zeek type ``connection``. This ID can be used only with + protocol analyzers. + + ``$file`` + Refers to the file that's currently being processed by + Zeek. On the Zeek-side this will turn into a parameter of + Zeek type ``fa_file``. This ID can be used only with file + analyzers. + + ``$is_orig`` + A boolean indicating if the data currently being processed + is coming from the originator (``True``) or responder + (``False``) of the underlying connection. This turns into + a corresponding boolean value on the Zeek side. This ID + can be used only with protocol analyzers. + + .. note:: + + Some tips: + + - If you want to force a specific type on the Zeek-side, you + have a couple of options: + + 1. Spicy may provide a ``cast`` operator from the actual + type into the desired type (e.g., ``cast(..)``). + + 2. Argument expressions have access to global functions + defined in the Spicy source files, so you can write a + conversion function taking an argument with its + original type and returning it with the desired type. + + - List comprehension can be convenient to fill Zeek vectors: + ``[some_func(i) for i in self.my_list]``. + +``if COND`` + If given, events are only generated if the expression ``COND`` + evaluates to true. Just like event arguments, the expression is + evaluated in the context of the current unit instance and has + access to ``self``. + +Importing Spicy Modules +----------------------- + +Code in an ``*.evt`` file may need access to additional Spicy modules, +such as when expressions for event parameters call Spicy +functions defined elsewhere. To make a Spicy module available, you can +insert ``import`` statements into the ``*.evt`` file that work +:ref:`just like in Spicy code `: + + ``import NAME`` + Imports Spicy module ``NAME``. + + ``import NAME from X.Y.Z;`` + Searches the module ``NAME`` (i.e., ``NAME.spicy``) inside a + sub-directory ``X/Y/Z`` along the search path, and then + imports it. + +.. _zeek_compiling: + +Compiling Analyzers +==================== + +Once you have the ``*.spicy`` and ``*.evt`` source files for your new +analyzer, you have two options to compile them, either just-in-time at +startup or in advance. + +Just In Time Compilation +------------------------ + +To compile analyzers on the fly, you just pass your ``*.spicy`` and +``*.evt`` files to Zeek just like any of its scripts, either on the +command-line or through ``@load`` statements. The Spicy plugin hooks +into Zeek's processing of input files and diverts them the right way +into its compilation pipeline. + +This approach can be quite convenient, in particular during +development of new analyzers as it makes it easy to iterate---just +restart Zeek to pick up any changes. The disadvantage is that +compiling Spicy parsers take a noticeable amount of time, which you'll +incur every time Zeek starts up. + +Ahead Of Time Compilation +------------------------- + +You can also precompile analyzers into ``*.hlto`` object files +containing their final executable code. To do that, pass the relevant +``*.spicy`` and ``*.evt`` files to ``spicyz``, then have Zeek load the +output. To repeat the :ref:`example ` from the +*Getting Started* guide:: + + # spicyz -o my-http-analyzer.hlto my-http.spicy my-http.evt + # zeek -Cr request-line.pcap my-http-analyzer.hlto my-http.zeek + GET, /index.html, 1.0 + Zeek saw from 127.0.0.1: GET /index.html 1.0 + +While this requires approach requires an additional step every time +something changes, starting up Zeek now executes quickly. + +Run ``spicyz -h`` to see some additional options it provides, which +are similar to :ref:`spicy-driver`. + +.. _zeek_functions: + +Controlling Zeek from Spicy +=========================== + +Spicy grammars can import a provided library module ``zeek`` to gain +access to a Zeek-specific functions that call back into Zeek's +processing: + +.. include:: /../html/autogen/zeek-functions.spicy + +.. _zeek_configuration: + +Configuration +============= + +The Spicy plugin provides a set of script-level options to tune its +behavior, similar to what the :ref:`spicy-driver` provides as +command-line arguments: + +.. literalinclude:: /../../zeek/plugin/scripts/base/spicy/main.zeek + :language: bro + :start-after: doc-start + :end-before: doc-end + +However, when changing options note that most values need to be in +effect at the time the Spicy plugin compiles any code. A ``redef`` +from another script should work fine, as scripts are fully processed +before compilation kicks off. However, changing values from the +command-line (via Zeek's ``var=value``) won't be processed in time due +to intricacies of Zeek's timing. Hence, to make it easier to change an +option from the command-line, the Spicy plugin also supports an +environment variable ``SPICY_PLUGIN_OPTIONS`` that accepts a subset of +``spicy-driver`` command-line options in the form of a string. For +example, to compile a debug version of all analyzers, set +``SPICY_PLUGIN_OPTIONS=-d``. The full set of options is this: + +.. code-block:: text + + Supported Zeek-side Spicy options: + -A When executing compiled code, abort() instead of throwing HILTI exceptions. + -B Include backtraces when reporting unhandled exceptions. + -C Dump all generated code to disk for debugging. + -d Include debug instrumentation into generated code. + -D Activate compile-time debugging output for given debug streams (comma-separated). + -O Build optimized release version of generated code. + -o Save precompiled code into file and exist. + -R Report a break-down of compiler's execution time. + -V Don't validate ASTs (for debugging only). + -X Implies -d and adds selected additional instrumentation (comma-separated). + +To get that usage message, run ``spicyz -h`` (recall that ``spicyz`` +is just a wrapper around Zeek). + +Debugging +========= + +If Zeek doesn't seem to be doing the right thing with your Spicy +analyzer, there are several ways to debug what's going on. To help +with that, make sure to compile your analyzer with ``spicy::debug=T`` +(or ``SPICY_PLUGIN_OPTIONS=-d``, or ``spicyz -d``); and, if possible, +use a debug version of Zeek (i.e., build Zeek with ``./configure +--enable-debug``). + +If your analyzer doesn't seem to be active at all, first make sure +Zeek actually knows about it: It should show up in the output of +``zeek -NN Zeek::Spicy``. If it doesn't, you might not being loading +the right ``*.spicy`` or ``*.evt`` files. Also check your ``*.evt`` if +it defines your analyzer correctly. + +If Zeek knows about your analyzer and just doesn't seem to activate +it, double-check that ports or MIME types are correct in the ``*.evt`` +file. If you're using a signature instead, try a port/MIME type first, +just to make sure it's not a matter of signature mismatches. + +If there's nothing obviously wrong with your source files, you can +trace what the plugin is compiling by running ``spicyz`` with ``-D +zeek``. For example, reusing the :ref:`HTTP example +` from the *Getting Started* guide:: + + # spicyz -D zeek my-http.spicy my-http.evt + [debug/zeek] Beginning pre-script initialization + [debug/zeek] Done with pre-script initialization + [debug/zeek] Beginning post-script initialization + [debug/zeek] Initializing driver + [debug/zeek] Loading input file "my-http.spicy" + [debug/zeek] Loading Spicy file "/home/robin/work/spicy/tests/spicy/doc/my-http.spicy" + [debug/zeek] Loading input file "my-http.evt" + [debug/zeek] Loading EVT file "/home/robin/work/spicy/doc/examples/my-http.evt" + [debug/zeek] Loading events from /home/robin/work/spicy/doc/examples/my-http.evt + [debug/zeek] Got protocol analyzer definition for spicy_MyHTTP + [debug/zeek] Got event definition for MyHTTP::request_line + [debug/zeek] Compiling input files + [debug/zeek] Running Spicy driver + [debug/zeek] Got unit type 'MyHTTP::Version' + [debug/zeek] Got unit type 'MyHTTP::RequestLine' + [debug/zeek] Adding protocol analyzer 'spicy_MyHTTP' + [debug/zeek] Adding Spicy hook 'MyHTTP::RequestLine::0x25_done' for event MyHTTP::request_line + [debug/zeek] Done with Spicy driver + [debug/zeek] Initializing Spicy runtime + [debug/zeek] Have Spicy protocol analyzer spicy_MyHTTP + [debug/zeek] Registering Protocol::TCP protocol analyzer spicy_MyHTTP with Zeek + [debug/zeek] Scheduling analyzer for port 12345/tcp + [debug/zeek] Done with post-script initialization + +You can see the main pieces in there: The files being loaded, unit +types provided by them, analyzers added to Zeek, and events that get +defined. + +.. note:: + + We're using ``spicyz`` in the example to precompile the code. You + can get the same debug output from Zeek itself when JITting the + inputs, but you'll need to pass the option in through the + environment by setting ``SPICY_PLUGIN_OPTIONS="-D zeek"``, + otherwise some parts will be missing. See + :ref:`zeek_configuration` for more on that. + +If that all looks as expected, it's time to turn to the Zeek side and +see what it's doing at runtime. You'll need a debug version of Zeek +for that, as well as a small trace with traffic that you expect your +analyzer to process. Run Zeek with ``-B dpd`` (or ``-B file_analysis`` +if you're debugging a file analyzer) on your trace to record the +analyzer activity into ``debug.log``. For example, with the same HTTP +example, we get: + +.. code-block:: text + :linenos: + + # zeek -B dpd -Cr request-line.pcap my-http.spicy my-http.evt + # cat debug.log + [dpd] Registering analyzer SPICY_MYHTTP for port 12345/1 + [...[ + [dpd] Available analyzers after zeek_init(): + [...] + [dpd] spicy_MyHTTP (enabled) + [...] + [dpd] Analyzers by port: + [dpd] 12345/tcp: SPICY_MYHTTP + [...] + [dpd] TCP[5] added child SPICY_MYHTTP[7] + [dpd] 127.0.0.1:59619 > 127.0.0.1:12345 activated SPICY_MYHTTP analyzer due to port 12345 + [...] + [dpd] SPICY_MYHTTP[7] DeliverStream(25, T) [GET /index.html HTTP/1.0\x0a] + [dpd] SPICY_MYHTTP[7] EndOfData(T) + [dpd] SPICY_MYHTTP[7] EndOfData(F) + +The first few lines show that Zeek's analyzer system registers the +analyzer as expected. The subsequent lines show that the analyzer gets +activated for processing the connection in the trace, and that it then +receives the data that we know indeed constitutes its payload, before +it eventually gets shutdown. + +To see this from the plugin's side at runtime, set the ``zeek`` debug +stream through the ``HILTI_DEBUG`` environment variable:: + + # HILTI_DEBUG=zeek SPICY_PLUGIN_OPTIONS="-d" zeek -Cr request-line.pcap my-http.spicy my-http.evt + [zeek] Have Spicy protocol analyzer spicy_MyHTTP + [zeek] Registering Protocol::TCP protocol analyzer spicy_MyHTTP with Zeek + [zeek] Scheduling analyzer for port 12345/tcp + [zeek] Done with post-script initialization + [zeek] [SPICY_MYHTTP/7/orig] initial chunk: |GET /index.html HTTP/1.0\\x0a| (eod=false) + [zeek] [SPICY_MYHTTP/7/orig] -> event MyHTTP::request_line($conn, GET, /index.html, 1.0) + [zeek] [SPICY_MYHTTP/7/orig] done with parsing + [zeek] [SPICY_MYHTTP/7/orig] parsing finished, skipping further originator payload + [zeek] [SPICY_MYHTTP/7/resp] no unit specificed for parsing + [zeek] [SPICY_MYHTTP/7/orig] skipping end-of-data delivery + [zeek] [SPICY_MYHTTP/7/resp] no unit specificed for parsing + [zeek] [SPICY_MYHTTP/7/orig] skipping end-of-data delivery + [zeek] [SPICY_MYHTTP/7/resp] no unit specificed for parsing + +After the initial initialization, you see the data arriving and the +event being generated for Zeek. The plugin also reports that we didn't +define a unit for the responder side---which we know in this case, but +if that comes unexpected you probably found a problem. + +.. note:: + + If you're running Zeek with ``SPICY_PLUGIN_OPTIONS="-D zeek -d"``, + you'll actually get the complete output of both what the compiler + is doing (per above) and what's happening at runtime. That may be + more convinient, but `HILTI_DEBUG` can do more (see below) and + might be the better starting point. + +So we know now that our analyzer is receiving the anticipated data to +parse. At this point, we can switch to debugging the Spicy side +:ref:`through the usual mechanisms `. In particular, +setting ``HILTI_DEBUG=spicy`` tends to be helpful:: + + # HILTI_DEBUG=spicy SPICY_PLUGIN_OPTIONS="-d" zeek -Cr request-line.pcap my-http.spicy my-http.evt + [spicy] MyHTTP::RequestLine + [spicy] method = GET + [spicy] anon_2 = + [spicy] uri = /index.html + [spicy] anon_3 = + [spicy] MyHTTP::Version + [spicy] anon = HTTP/ + [spicy] number = 1.0 + [spicy] version = [$number=b"1.0"] + [spicy] anon_4 = \n + +If everything looks right with the parsing, and the right events are +genreated too, then the final part is to check out the events that +arrive on the Zeek side. To get Zeek to see an event that the plugin +raises, you need to have at least one handler implemented for it in +one of your Zeek scripts. You can then load Zeek's +``misc/dump-events`` to see them as they are being received, including +their full Zeek-side values:: + + # zeek -Cr request-line.pcap my-http.spicy my-http.evt misc/dump-events + [...] + 1580991211.780489 MyHTTP::request_line + [0] c: connection = [id=[orig_h=127.0.0.1, orig_p=59619/tcp, ...] ...] + [1] method: string = GET + [2] uri: string = /index.html + [3] version: string = 1.0 + [...] diff --git a/docker/Dockerfile.alpine-3.11 b/docker/Dockerfile.alpine-3.11 new file mode 100644 index 000000000..c77ea660c --- /dev/null +++ b/docker/Dockerfile.alpine-3.11 @@ -0,0 +1,30 @@ +FROM alpine:3.11 + +WORKDIR /root + +ENV PATH="/opt/spicy/bin:/opt/zeek/bin:${PATH}" +ENV ZEEK_PLUGIN_PATH="/opt/spicy/lib64/spicy/" + +RUN apk update + +# Install development tools. +RUN apk add ccache cmake curl g++ gcc gdb git make ninja python3 vim + +# Install Spicy dependencies. +RUN apk add bash bison clang clang-dev clang-libs clang-static flex flex-dev flex-libs libucontext-dev llvm9-dev llvm9-static py3-sphinx py3-sphinx_rtd_theme +RUN pip3 install btest + +# Install Zeek dependencies. +RUN apk add fts-dev libpcap-dev linux-headers openssl-dev python-dev swig zlib-dev + +# Install Zeek. +RUN mkdir -p /opt/zeek/src +RUN cd /opt/zeek && git clone -b release/3.0 --recursive https://github.com/zeek/zeek src +RUN cd /opt/zeek/src && ./configure --generator=Ninja --prefix=/opt/zeek && cd build && ninja && ninja install && cd .. && rm -rf build + +# Install Spicy. +ADD . /opt/spicy/src + +RUN cd /opt/spicy/src && \ + LDFLAGS="-lucontext" ./configure --prefix=/opt/spicy --generator=Ninja --with-cxx-compiler=/usr/bin/clang++ --with-zeek=/opt/zeek --disable-gold && \ + cd build && ninja install && cd .. && rm -rf build diff --git a/docker/Dockerfile.centos-8 b/docker/Dockerfile.centos-8 new file mode 100644 index 000000000..1bbe562ae --- /dev/null +++ b/docker/Dockerfile.centos-8 @@ -0,0 +1,49 @@ +FROM centos:8 + +WORKDIR /root + +ENV PATH="/opt/spicy/bin:/opt/zeek/bin:${PATH}" +ENV ZEEK_PLUGIN_PATH="/opt/spicy/lib64/spicy/" + +RUN yum install -y epel-release yum-utils && yum-config-manager --set-enabled PowerTools +RUN yum update -y + +# Install development tools. +RUN yum install -y ccache gcc gcc-c++ gdb git make ninja-build python3 python3-pip vim + + # Need a more recent CMake than available. +RUN cd /usr/local && curl -L https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-Linux-x86_64.tar.gz | tar xzvf - && ln -s /usr/local/cmake-3.16.4-Linux-x86_64/bin/cmake /usr/local/bin + +# Need to compile Clang, there don't seem to be packages out there for v9. +RUN mkdir -p /opt/clang9/src && \ + cd /opt/clang9/src && \ + git clone --branch release/9.x --single-branch --recursive https://github.com/llvm/llvm-project.git && \ + cd llvm-project && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_INSTALL_PREFIX=/opt/clang9 -DLLVM_ENABLE_PROJECTS="clang;compiler-rt" -DLLVM_TARGETS_TO_BUILD=host -DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON -G Ninja ../llvm && \ + ninja install && \ + cd ../.. && \ + rm -rf /opt/clang9/src + +# Install Spicy dependencies. +RUN yum install -y flex python3-sphinx +RUN pip3 install btest sphinx-rtd-theme + + # Need a more recnet Bison than available. +RUN cd /opt && curl -L http://ftp.gnu.org/gnu/bison/bison-3.5.tar.gz | tar xzvf - && cd /opt/bison-3.5 && ./configure && make install + +# Install Zeek dependencies. +RUN yum install -y libpcap-devel openssl-devel python3-devel swig zlib-devel + +# Install Zeek. +RUN mkdir -p /opt/zeek/src +RUN cd /opt/zeek && git clone -b release/3.0 --recursive https://github.com/zeek/zeek src +RUN cd /opt/zeek/src && ./configure --generator=Ninja --prefix=/opt/zeek && cd build && ninja && ninja install && cd .. && rm -rf build + +# Install Spicy. +ADD . /opt/spicy/src + +RUN cd /opt/spicy/src && \ + ./configure --prefix=/opt/spicy --with-zeek=/opt/zeek --with-cxx-compiler=/opt/clang9/bin/clang++ --generator=Ninja && \ + cd build && ninja install && cd .. && rm -rf build diff --git a/docker/Dockerfile.ubuntu-19.10 b/docker/Dockerfile.ubuntu-19.10 new file mode 100644 index 000000000..b4707ba13 --- /dev/null +++ b/docker/Dockerfile.ubuntu-19.10 @@ -0,0 +1,34 @@ +FROM ubuntu:19.10 + +WORKDIR /root + +ENV PATH="/opt/spicy/bin:/opt/zeek/bin:${PATH}" +ENV ZEEK_PLUGIN_PATH="/opt/spicy/lib64/spicy/" +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update + +# Install development tools. +RUN apt-get install -y ccache curl g++ gcc gdb git locales-all make ninja-build python3 python3-pip vim + + # Need a more recent CMake than available. +RUN cd /usr/local && curl -L https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-Linux-x86_64.tar.gz | tar xzvf - && ln -s /usr/local/cmake-3.16.4-Linux-x86_64/bin/cmake /usr/local/bin + +# Install Spicy dependencies. +RUN apt-get install -y bison clang-9 flex libclang-9-dev python3-sphinx python3-sphinx-rtd-theme +RUN pip3 install btest + +# Install Zeek dependencies. +RUN apt-get install -y libpcap-dev libssl-dev python-dev swig zlib1g-dev + +# Install Zeek. +RUN mkdir -p /opt/zeek/src +RUN cd /opt/zeek && git clone -b release/3.0 --recursive https://github.com/zeek/zeek src +RUN cd /opt/zeek/src && ./configure --generator=Ninja --prefix=/opt/zeek && cd build && ninja && ninja install && cd .. && rm -rf build + +# Install Spicy. +ADD . /opt/spicy/src + +RUN cd /opt/spicy/src && \ + ./configure --prefix=/opt/spicy --with-zeek=/opt/zeek --generator=Ninja && \ + cd build && ninja install && cd .. && rm -rf build diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 000000000..ea95082b9 --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,39 @@ + +all: help + +help: + @echo + @echo 'Run "make build-", then optionally "make test-",' + @echo 'and finally "make run-".' + @echo + @echo Available platforms: + @echo + @cat Makefile | awk -F '[-:]' '/^build-/ { printf(" %s-%s\n", $$2, $$3); }' | sort + @echo + +build-ubuntu-19.10: + ./docker-helper build ubuntu-19.10 + +test-ubuntu-19.10: build-ubuntu-19.10 + ./docker-helper test ubuntu-19.10 + +run-ubuntu-19.10: + ./docker-helper run ubuntu-19.10 + +build-alpine-3.11: + ./docker-helper build alpine-3.11 + +test-alpine-3.11: build-alpine-3.11 + ./docker-helper test alpine-3.11 + +run-alpine-3.11: + ./docker-helper run alpine-3.11 + +build-centos-8: + ./docker-helper build centos-8 + +test-centos-8: build-centos-8 + ./docker-helper test centos-8 + +run-centos-8: + ./docker-helper run centos-8 diff --git a/docker/docker-helper b/docker/docker-helper new file mode 100755 index 000000000..681308ab9 --- /dev/null +++ b/docker/docker-helper @@ -0,0 +1,36 @@ +#! /bin/sh +# +# Helper for the Makefile to trigger Docker commands with the desired image. + +usage() { + echo "usage: $(basename $0) build|test|run " + exit 1 +} + +test $# = 2 || usage + +version=$(../scripts/autogen-version --short) +cmd=$1 +platform=$2 + +if [ ! -e Dockerfile.${platform} ]; then + echo "Dockerfile.${platform} does not exist" + exit 1 +fi + +case "${cmd}" in + build) + DOCKER_BUILDKIT=1 docker build -t spicy-${platform}:${version} -f Dockerfile.${platform} .. || exit 1 + docker tag $(docker inspect --format='{{.Id}}' spicy-${platform}:${version}) spicy-${platform}:latest + ;; + + test) + docker run spicy-${platform}:latest /bin/sh -c "cd /opt/spicy/src/tests && SPICY_INSTALLATION_DIRECTORY=/opt/spicy btest -a installation -q -j -d" + ;; + + run) + docker run --cap-add SYS_PTRACE -i -t spicy-${platform}:latest /bin/bash + ;; + + *) usage;; +esac diff --git a/hilti/CMakeLists.txt b/hilti/CMakeLists.txt new file mode 100644 index 000000000..a3d2af986 --- /dev/null +++ b/hilti/CMakeLists.txt @@ -0,0 +1,289 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +##### Compiler library. + +set(AUTOGEN_H "${CMAKE_CURRENT_BINARY_DIR}/include/hilti/autogen") +set(AUTOGEN_RT_H "${CMAKE_CURRENT_BINARY_DIR}/include/hilti/rt/autogen") +set(AUTOGEN_CC "${CMAKE_CURRENT_BINARY_DIR}/src/autogen") +file(MAKE_DIRECTORY "${AUTOGEN_H}" "${AUTOGEN_RT_H}" "${AUTOGEN_CC}") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" "${CMAKE_CURRENT_BINARY_DIR}/lib") + +add_custom_target(version + ALL + COMMAND ${PROJECT_SOURCE_DIR}/scripts/autogen-version --header ${AUTOGEN_RT_H}/version.h + BYPRODUCTS ${AUTOGEN_RT_H}/version.h) + +FLEX_TARGET(scanner_hilti src/compiler/parser/scanner.ll ${AUTOGEN_CC}/__scanner.cc + DEFINES_FILE ${AUTOGEN_CC}/__scanner.h) +BISON_TARGET(parser_hilti src/compiler/parser/parser.yy ${AUTOGEN_CC}/__parser.cc + DEFINES_FILE ${AUTOGEN_CC}/__parser.h +) + +bison_source(src/compiler/parser/driver.cc ${AUTOGEN_CC}) +bison_source(${AUTOGEN_CC}/__scanner.cc ${AUTOGEN_CC}) +bison_source(${AUTOGEN_CC}/__parser.cc ${AUTOGEN_CC}) + +include(TypeErase) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/node.api NO) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/ctor.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/declaration.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/expression.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/expressions/resolved-operator.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/operator.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/statement.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/hilti/ast/type.api YES) + +include(ASTOperators) +autogen_operators(SOURCES_OPERATORS + hilti + include/hilti/ast/operators + ${AUTOGEN_H}/operators.decl + ${AUTOGEN_CC}/operators-implementations.cc +) + +autogen_dispatchers(SOURCES_TYPE_ERASED ${AUTOGEN_H}/__dispatchers.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/hilti/ast/nodes.decl + ${AUTOGEN_H}/operators.decl) + +set(SOURCES_COMPILER + src/ast/builder/builder.cc + src/ast/expression.cc + src/ast/expressions + src/ast/expressions/id.cc + src/ast/location.cc + src/ast/module.cc + src/ast/node.cc + src/ast/node_ref.cc + src/ast/scope.cc + src/ast/type.cc + src/ast/types/enum.cc + src/ast/types/integer.cc + src/ast/types/tuple.cc + src/base/code-formatter.cc + src/base/logger.cc + src/base/timing.cc + src/base/type_erase.cc + src/base/util.cc + src/compiler/codegen/codegen.cc + src/compiler/codegen/coercions.cc + src/compiler/codegen/ctors.cc + src/compiler/codegen/expressions.cc + src/compiler/codegen/expressions.cc + src/compiler/codegen/operators.cc + src/compiler/codegen/statements.cc + src/compiler/codegen/types.cc + src/compiler/codegen/unpack.cc + src/compiler/coercion.cc + src/compiler/context.cc + src/compiler/cxx/elements.cc + src/compiler/cxx/formatter.cc + src/compiler/cxx/linker.cc + src/compiler/cxx/unit.cc + src/compiler/driver.cc + src/compiler/jit.cc + src/compiler/parser/driver.cc + src/compiler/plugin.cc + src/compiler/unit.cc + src/compiler/visitors/coercer.cc + src/compiler/visitors/apply-coercions.cc + src/compiler/visitors/coercer.cc + src/compiler/visitors/importer.cc + src/compiler/visitors/printer.cc + src/compiler/visitors/renderer.cc + src/compiler/visitors/id-resolver.cc + src/compiler/visitors/operator-resolver.cc + src/compiler/visitors/scope-builder.cc + src/compiler/visitors/validator.cc + src/global.cc + + $<$:src/compiler/clang.cc> + + # Already included in hilti-rt, which we pull in. + # src/3rdparty/utf8proc/utf8proc.c + src/3rdparty/pathfind/src/PathFind.cpp + + ${SOURCES_TYPE_ERASED} + ${SOURCES_OPERATORS} + + ${AUTOGEN_CC}/config.cc + ${BISON_parser_hilti_OUTPUTS} + ${FLEX_scanner_hilti_OUTPUTS} + ) + +# Getting a link error without this. +# (lib64/libhilti.so: undefined reference to `llvm::cfg::Update::dump() const') +set_source_files_properties(src/compiler/clang.cc PROPERTIES COMPILE_DEFINITIONS "NDEBUG") + +add_library(hilti ${SOURCES_COMPILER}) +add_dependencies(hilti version) +target_compile_options(hilti PRIVATE "-Wall") +target_link_libraries(hilti PRIVATE $,hilti-rt-debug-objects,hilti-rt-objects>) +target_link_libraries(hilti PRIVATE $<$:clang-jit>) +target_link_libraries(hilti PRIVATE std::filesystem ${CMAKE_DL_LIBS}) +target_include_directories(hilti PUBLIC $) +target_include_directories(hilti PUBLIC $) + +# Unclear why we need this: Without it, the generated Bison/Flex get a broken +# include path on some systems. (Seen on Ubuntu 19.10). +set_target_properties(hilti PROPERTIES NO_SYSTEM_FROM_IMPORTED true) + +##### Runtime library. + +add_subdirectory(src/3rdparty/justrx) + +set(SOURCES_RUNTIME + src/rt/backtrace.cc + src/rt/configuration.cc + src/rt/context.cc + src/rt/debug-logger.cc + src/rt/exception.cc + src/rt/fiber.cc + src/rt/global-state.cc + src/rt/hilti.cc + src/rt/init.cc + src/rt/linker.cc + src/rt/logging.cc + src/rt/main.cc + src/rt/types/address.cc + src/rt/types/bytes.cc + src/rt/types/integer.cc + src/rt/types/port.cc + src/rt/types/real.cc + src/rt/types/regexp.cc + src/rt/types/stream.cc + src/rt/types/string.cc + src/rt/types/time.cc + src/rt/util.cc + + src/3rdparty/libtask/asm.S + src/3rdparty/libtask/context.c + src/3rdparty/utf8proc/utf8proc.c +) + +foreach ( lib hilti-rt hilti-rt-debug ) + add_library(${lib}-objects OBJECT ${SOURCES_RUNTIME}) + add_dependencies(${lib}-objects version) + target_compile_options(${lib}-objects PRIVATE "-fPIC") + target_link_libraries(${lib}-objects PRIVATE std::filesystem) + target_include_directories(${lib}-objects PUBLIC $) + target_include_directories(${lib}-objects PUBLIC $) + target_include_directories(${lib}-objects PRIVATE include/3rdparty/libtask) + + # The following should instead just be: + # + # target_link_libraries(${lib}-objects PRIVATE jrx-objects) + # + # but does not pull in the object files for some reason. Since at least + # with cmake-3.16.5 depending on some `TARGET_OBJECTS` does not ensure the + # referenced target is built before the dependent target is linked, we also + # explicitly depend on jrx-objects to make sure all its objects are build. + add_dependencies(${lib}-objects jrx-objects) + target_include_directories(${lib}-objects PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/3rdparty/justrx/include) + target_link_libraries(${lib}-objects PRIVATE $) + + add_library(${lib} STATIC) + target_link_libraries(${lib} ${lib}-objects) +endforeach () + +# Build hilti-rt with release flags. +string(REPLACE " " ";" cxx_flags_release ${CMAKE_CXX_FLAGS_RELEASE}) +target_compile_options(hilti-rt-objects PRIVATE ${cxx_flags_release}) +target_compile_options(hilti-rt-objects PRIVATE "-DNDEBUG;-O3;-g0;-Wall") +target_compile_definitions(hilti-rt-objects PRIVATE "HILTI_RT_BUILD_TYPE_RELEASE") + +# Build hilti-rt-debug with debug flags. +string(REPLACE " " ";" cxx_flags_debug ${CMAKE_CXX_FLAGS_DEBUG}) +target_compile_options(hilti-rt-debug-objects PRIVATE ${cxx_flags_debug}) +target_compile_options(hilti-rt-debug-objects PRIVATE "-UNDEBUG;-O0;-Wall") +target_compile_definitions(hilti-rt-debug-objects PRIVATE "HILTI_RT_BUILD_TYPE_DEBUG") + +##### Configuration files + +# HILTI library directories +set_config_val(HILTI_CONFIG_LIBRARY_DIRS "!INSTALL!${CMAKE_INSTALL_FULL_DATADIR}/hilti !BUILD!${CMAKE_CURRENT_SOURCE_DIR}/lib") + +# Include directories + +set_config_val(HILTI_CONFIG_RUNTIME_INCLUDE_DIRS_DEBUG "!INSTALL!${CMAKE_INSTALL_FULL_INCLUDEDIR} !BUILD!${CMAKE_CURRENT_SOURCE_DIR}/include !BUILD!${CMAKE_CURRENT_BINARY_DIR}/include") +set_config_val(HILTI_CONFIG_RUNTIME_INCLUDE_DIRS_RELEASE "!INSTALL!${CMAKE_INSTALL_FULL_INCLUDEDIR} !BUILD!${CMAKE_CURRENT_SOURCE_DIR}/include !BUILD!${CMAKE_CURRENT_BINARY_DIR}/include") + +# CXX flags + +if ( APPLE ) + set(addl_cxx_flags "${cxx_flags} -isysroot ${CMAKE_OSX_SYSROOT}") +endif () + +set_config_val(HILTI_CONFIG_RUNTIME_CXX_FLAGS_DEBUG "-std=c++17 -g ${addl_cxx_flags} ${EXTRA_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") +set_config_val(HILTI_CONFIG_RUNTIME_CXX_FLAGS_RELEASE "-std=c++17 -O3 -DNDEBUG ${addl_cxx_flags} ${EXTRA_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") + +# Libraries + +get_target_property(filesystem_library std::filesystem INTERFACE_LINK_LIBRARIES) +if ( NOT filesystem_library ) + set(filesystem_library "") +endif () + +string(REPLACE "-l" "" fslib "${filesystem_library}") +set_config_val(HILTI_CONFIG_RUNTIME_LIBRARIES_DEBUG "hilti-rt-debug ${fslib}") +set_config_val(HILTI_CONFIG_RUNTIME_LIBRARIES_RELEASE "hilti-rt ${fslib}") + +# Library directories + +set_config_val(HILTI_CONFIG_RUNTIME_LIBRARY_DIRS_DEBUG "!BUILD!${CMAKE_LIBRARY_OUTPUT_DIRECTORY} !INSTALL!${CMAKE_INSTALL_FULL_LIBDIR}") +set_config_val(HILTI_CONFIG_RUNTIME_LIBRARY_DIRS_RELEASE "!BUILD!${CMAKE_LIBRARY_OUTPUT_DIRECTORY} !INSTALL!${CMAKE_INSTALL_FULL_LIBDIR}") + +# LD flags + +set_config_val(HILTI_CONFIG_RUNTIME_LD_FLAGS_DEBUG "${EXTRA_LD_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_INIT}") +set_config_val(HILTI_CONFIG_RUNTIME_LD_FLAGS_RELEASE "${EXTRA_LD_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_INIT}") + +# Generate configurations + +configure_file(include/config.h.in ${AUTOGEN_H}/config.h) +configure_file(include/rt/config.h.in ${AUTOGEN_RT_H}/config.h) +configure_file(src/config.cc.in ${AUTOGEN_CC}/config.cc) + +##### Binaries + +add_executable(hiltic bin/hiltic.cc) +target_compile_options(hiltic PRIVATE "-Wall") +target_link_libraries(hiltic PRIVATE hilti) + +add_executable(hilti-config bin/hilti-config.cc) +target_compile_options(hilti-config PRIVATE "-Wall") +target_link_libraries(hilti-config PRIVATE hilti) + +add_executable(jit-test bin/jit-test.cc) +target_compile_options(jit-test PRIVATE "-Wall") +target_link_libraries(jit-test PRIVATE hilti) + +# Tests +add_executable(hilti-rt-tests + src/rt/tests/main.cc + src/rt/tests/address.cc + src/rt/tests/fiber.cc + src/rt/tests/reference.cc + src/rt/tests/result.cc + src/rt/tests/stream.cc) +target_compile_options(hilti-rt-tests PRIVATE "-Wall") +target_link_libraries(hilti-rt-tests PRIVATE hilti-rt doctest) +add_test(NAME hilti-rt-tests COMMAND ${CMAKE_BINARY_DIR}/bin/hilti-rt-tests) + +add_executable(hilti-tests + tests/main.cc + tests/visitor.cc + tests/util.cc) +target_link_libraries(hilti-tests PRIVATE hilti doctest) +target_compile_options(hilti-tests PRIVATE "-Wall") +add_test(NAME hilti-tests COMMAND ${CMAKE_BINARY_DIR}/bin/hilti-tests) + +## Installation + +install(TARGETS hilti LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS hilti-rt hilti-rt-debug ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS hiltic hilti-config RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib/ DESTINATION ${CMAKE_INSTALL_DATADIR}/hilti MESSAGE_NEVER) + +install_headers(include hilti) +install_headers(${CMAKE_CURRENT_BINARY_DIR}/include/hilti hilti) +install(CODE "file(REMOVE ${CMAKE_INSTALL_FULL_INCLUDEDIR}/hilti/hilti)") # Get rid of symlink diff --git a/hilti/bin/hilti-config.cc b/hilti/bin/hilti-config.cc new file mode 100644 index 000000000..509a65b86 --- /dev/null +++ b/hilti/bin/hilti-config.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +/// +/// Outputs paths and flags for using HILTI. +/// +/// TODO: Currently we do not support installation outside of the built-tree +/// and all values returned here are thus in-tree. + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; + +void usage() { + std::cerr << R"( +Usage: hilti-config [options] + +Available options: + + --build Prints "debug" or "release", depending on the build configuration. + --cxx Print the full path to the compiler used to compile HILTI. + --cxxflags Print C++ flags when compiling code using the HILTI runtime library + --debug Output flags for working with debugging versions. + --distbase Print path of the HILTI source distribution. + --help Print this usage summary + --hiltic Print the full path to the hiltic binary. + --jit-compiler Prints the version of the JIT compiler if compiled with corresponding support. + --jit-support Prints 'yes' if compiled with JIT support, 'no' otherwise. + --ldflags Print linker flags when linking code using the HILTI runtime library + --libdirs Print standard HILTI library directories. + --prefix Print path of installation. + --version Print HILTI version. + + --using-build-dir Returns true when hilti-config's output is referring to the build directory; + and false when refering to the installation +)"; +} + +template +void join(std::vector& a, const std::vector& b) { + a.insert(a.end(), b.begin(), b.end()); +} + +int main(int argc, char** argv) { + bool want_debug = false; + + std::list cxxflags; + std::list ldflags; + + std::list options; + + // First pass over arguments: look for control options. + + for ( int i = 1; i < argc; i++ ) { + string opt = argv[i]; + + if ( opt == "--help" || opt == "-h" ) { + usage(); + return 0; + } + + if ( opt == "--debug" ) { + want_debug = true; + continue; + } + + options.push_back(opt); + } + + std::vector result; + + for ( const auto& opt : options ) { + if ( opt == "--distbase" ) { + result.emplace_back(hilti::configuration().distbase); + continue; + } + + if ( opt == "--prefix" ) { + result.emplace_back(hilti::configuration().install_prefix); + continue; + } + + if ( opt == "--version" ) { + result.emplace_back(hilti::configuration().version_string_long); + continue; + } + + if ( opt == "--build" ) { +#ifndef NDEBUG + result.emplace_back("debug"); +#else + result.emplace_back("release"); +#endif + continue; + } + + if ( opt == "--jit-compiler" ) { + result.emplace_back(hilti::JIT::compilerVersion()); + continue; + } + + if ( opt == "--jit-support" ) { + result.emplace_back((hilti::configuration().jit_enabled ? "yes" : "no")); + continue; + } + + if ( opt == "--cxx" ) { + result.emplace_back(hilti::configuration().cxx); + continue; + } + + if ( opt == "--hiltic" ) { + result.emplace_back(hilti::configuration().hiltic); + continue; + } + + if ( opt == "--libdirs" ) { + join(result, hilti::configuration().hilti_library_paths); + continue; + } + + if ( opt == "--cxxflags" ) { + if ( want_debug ) + join(result, hilti::configuration().runtime_cxx_flags_debug); + else + join(result, hilti::configuration().runtime_cxx_flags_release); + + continue; + } + + if ( opt == "--ldflags" ) { + if ( want_debug ) + join(result, hilti::configuration().runtime_ld_flags_debug); + else + join(result, hilti::configuration().runtime_ld_flags_release); + + continue; + } + + if ( opt == "--using-build-dir" ) + exit(hilti::configuration().uses_build_directory ? 0 : 1); + + std::cerr << "hilti-config: unknown option " << opt << "; use --help to see list." << std::endl; + return 1; + } + + cout << util::join(result.begin(), result.end(), " ") << std::endl; + + return 0; +} diff --git a/hilti/bin/hiltic.cc b/hilti/bin/hiltic.cc new file mode 100644 index 000000000..5b570a379 --- /dev/null +++ b/hilti/bin/hiltic.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +int main(int argc, char** argv) { + hilti::Driver driver("hiltic"); + + if ( auto rc = driver.parseOptions(argc, argv); ! rc ) { + hilti::logger().error(rc.error().description()); + exit(1); + } + + if ( auto rc = driver.run(); ! rc ) { + hilti::logger().error(rc.error().description()); + exit(1); + } + + return 0; +} diff --git a/hilti/bin/jit-test.cc b/hilti/bin/jit-test.cc new file mode 100644 index 000000000..4b64e0030 --- /dev/null +++ b/hilti/bin/jit-test.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +int main(int argc, char** argv) { + if ( argc != 2 ) { + std::cerr << "Usage: jit-test " << std::endl; + return 1; + } + + hilti::CxxCode code(argv[1]); + + if ( ! code.isLoaded() ) { + std::cerr << "Could not load source file" << std::endl; + return 1; + } + + hilti::Options options; + auto ctx = std::make_shared(options); + + hilti::JIT compiler(ctx); + compiler.add(code); + + if ( ! compiler.compile() ) { + std::cerr << "Could not compile source file" << std::endl; + return 1; + } + + return 0; +} diff --git a/hilti/include/3rdparty/SafeInt/LICENSE b/hilti/include/3rdparty/SafeInt/LICENSE new file mode 100644 index 000000000..c75738365 --- /dev/null +++ b/hilti/include/3rdparty/SafeInt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Microsoft + +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/hilti/include/3rdparty/SafeInt/README.hilti b/hilti/include/3rdparty/SafeInt/README.hilti new file mode 100644 index 000000000..3baf8588a --- /dev/null +++ b/hilti/include/3rdparty/SafeInt/README.hilti @@ -0,0 +1,5 @@ +A class library for C++ that manages integer overflows. From +https://github.com/dcleblanc/SafeInt. + +Changes: + - We remove the artificial operator& overload, as we need the actual one. diff --git a/hilti/include/3rdparty/SafeInt/README.md b/hilti/include/3rdparty/SafeInt/README.md new file mode 100644 index 000000000..b5a87a506 --- /dev/null +++ b/hilti/include/3rdparty/SafeInt/README.md @@ -0,0 +1,7 @@ +# SafeInt +SafeInt is a class library for C++ that manages integer overflows. + +Now updated to keep the entire history of the class from the time it first was checked into CodePlex. + +March, 2018 - added support for constexpr, also enforcing a requirement that --std=c++11 or --std=c++14 +has to be used for gcc or clang, minimum Visual Studio compiler version TBD. diff --git a/hilti/include/3rdparty/SafeInt/SafeInt.hpp b/hilti/include/3rdparty/SafeInt/SafeInt.hpp new file mode 100644 index 000000000..3c5732183 --- /dev/null +++ b/hilti/include/3rdparty/SafeInt/SafeInt.hpp @@ -0,0 +1,7105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/*----------------------------------------------------------------------------------------------------------- +SafeInt.hpp +Version 3.0.20p + +This header implements an integer handling class designed to catch +unsafe integer operations + +This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang. +Tested most recently on clang 3.8.0, gcc 7.3.1, and both Visual Studio 2015 and 2017. + +Please read the leading comments before using the class. +---------------------------------------------------------------*/ +#ifndef SAFEINT_HPP +#define SAFEINT_HPP + +// It is a bit tricky to sort out what compiler we are actually using, +// do this once here, and avoid cluttering the code +#define VISUAL_STUDIO_COMPILER 0 +#define CLANG_COMPILER 1 +#define GCC_COMPILER 2 +#define UNKNOWN_COMPILER -1 + +// Clang will sometimes pretend to be Visual Studio +// and does pretend to be gcc. Check it first, as nothing else pretends to be clang +#if defined __clang__ +#define SAFEINT_COMPILER CLANG_COMPILER +#elif defined __GNUC__ +#define SAFEINT_COMPILER GCC_COMPILER +#elif defined _MSC_VER +#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER +#else +#define SAFEINT_COMPILER UNKNOWN_COMPILER +#endif + +#define CPLUSPLUS_98 0 +#define CPLUSPLUS_11 1 +#define CPLUSPLUS_14 2 +#define CPLUSPLUS_17 3 // Future use + +// Determine C++ support level +#if SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER + +#if __cplusplus < 201103L +#define CPLUSPLUS_STD CPLUSPLUS_98 +#elif __cplusplus < 201402L +#define CPLUSPLUS_STD CPLUSPLUS_11 +#else +#define CPLUSPLUS_STD CPLUSPLUS_14 +#endif + +#elif SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER + +// This needs additional testing to get more versions of _MSCVER +#if _MSC_VER < 1900 // Prior to VS 2015, need more testing to determine support +#define CPLUSPLUS_STD CPLUSPLUS_98 + +#elif _MSC_VER < 1910 // VS 2015 +#define CPLUSPLUS_STD CPLUSPLUS_11 + +#else // VS 2017 or later +// Note - there is a __cpp_constexpr test now, but everything prior to VS 2017 reports incorrect values +// and this version always supports at least the CPLUSPLUS_14 approach +#define CPLUSPLUS_STD CPLUSPLUS_14 + +#endif + +#else +// Unknown compiler, assume C++ 98 +#define CPLUSPLUS_STD CPLUSPLUS_98 +#endif // Determine C++ support level + +#if (SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER) && CPLUSPLUS_STD < CPLUSPLUS_11 +#error Must compile with --std=c++11, preferably --std=c++14 to use constexpr improvements +#endif + +#define CONSTEXPR_NONE 0 +#define CONSTEXPR_CPP11 1 +#define CONSTEXPR_CPP14 2 + +// Let's try to use the new standard to determine feature compliance +// If the user has an unknown compiler, or just for testing, allow forcing this setting +#if !defined CONSTEXPR_SUPPORT + +#if defined __cpp_constexpr +// If it is gcc or clang, at least recent versions, then we have -std=c++11 or -std=c++14 +// This won't be set otherwise, but the headers won't compile, either +#if __cpp_constexpr >= 201304L +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP14 // Clang, gcc, Visual Studio 2017 or later +#elif __cpp_constexpr >= 200704L +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP11 // Clang, gcc with -std=c++11, Visual Studio 2015 +#else +#define CONSTEXPR_SUPPORT CONSTEXPR_NONE +#endif + +#else // !defined __cpp_constexpr +// Visual Studio is somehow not playing nice. shows __cpp_constexpr visually as defined, but won't compile +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#if CPLUSPLUS_STD == CPLUSPLUS_14 +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP14 +#elif CPLUSPLUS_STD == CPLUSPLUS_11 +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP11 +#else +#define CONSTEXPR_SUPPORT CONSTEXPR_NONE +#endif +#else +#define CONSTEXPR_SUPPORT CONSTEXPR_NONE +#endif + +#endif // defined __cpp_constexpr + +#endif // !defined CONSTEXPR_SUPPORT + + +#if CONSTEXPR_SUPPORT == CONSTEXPR_NONE +#define _CONSTEXPR11 +#define _CONSTEXPR14 +#elif CONSTEXPR_SUPPORT == CONSTEXPR_CPP11 +#define _CONSTEXPR11 constexpr +#define _CONSTEXPR14 +#elif CPLUSPLUS_STD >= CPLUSPLUS_14 +#define _CONSTEXPR11 constexpr +#define _CONSTEXPR14 constexpr +#else +#error "Unexpected value of CPLUSPLUS_STD" +#endif + +// Enable compiling with /Wall under VC +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +// Off by default - unreferenced inline function has been removed +// Note - this intentionally leaks from the header, doesn't quench the warnings otherwise +#pragma warning( disable: 4514 ) + +#pragma warning( push ) +// Disable warnings coming from headers +#pragma warning( disable:4987 4820 4987 4820 ) +#endif + +// More defines to accomodate compiler differences +#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER +#define SAFEINT_NORETURN __attribute__((noreturn)) +#define SAFEINT_STDCALL +#define SAFEINT_VISIBLE __attribute__ ((__visibility__("default"))) +#define SAFEINT_WEAK __attribute__ ((weak)) +#else +#define SAFEINT_NORETURN __declspec(noreturn) +#define SAFEINT_STDCALL __stdcall +#define SAFEINT_VISIBLE +#define SAFEINT_WEAK +#endif + +// On the Microsoft compiler, violating a throw() annotation is a silent error. +// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled. +// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw. +#if defined SAFEINT_REMOVE_NOTHROW +#define SAFEINT_NOTHROW +#else +#define SAFEINT_NOTHROW noexcept +#endif + +#include +#include +#include // This is now required +// Need this for ptrdiff_t on some compilers +#include +#include // Needed for floating point implementation + +// Note - intrinsics and constexpr are mutually exclusive +// If it is important to get constexpr for multiplication, then define SAFEINT_USE_INTRINSICS 0 +// However, intrinsics will result in much smaller code, and should have better perf +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 && !defined SAFEINT_USE_INTRINSICS + #include + #define SAFEINT_USE_INTRINSICS 1 + #define _CONSTEXPR14_MULTIPLY +#else + #define SAFEINT_USE_INTRINSICS 0 + #define _CONSTEXPR14_MULTIPLY _CONSTEXPR14 +#endif + +// If you would like to use your own custom assert +// Define SAFEINT_ASSERT +#if !defined SAFEINT_ASSERT +#include +#define SAFEINT_ASSERT(x) assert(x) +#endif + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning( pop ) +#endif + +#if !defined _CRT_SECURE_INVALID_PARAMETER +// Calling fail fast is somewhat more robust than calling abort, +// but abort is the closest we can manage without Visual Studio support +// Need the header for abort() +#include +#define _CRT_SECURE_INVALID_PARAMETER(msg) abort() +#endif + +// Let's test some assumptions +// We're assuming two's complement negative numbers +static_assert( -1 == static_cast(0xffffffff), "Two's complement signed numbers are required" ); + +/************* Compiler Options ***************************************************************************************************** + +SafeInt supports several compile-time options that can change the behavior of the class. + +Compiler options: +SAFEINT_ASSERT_ON_EXCEPTION - it is often easier to stop on an assert and figure out a problem than to try and figure out + how you landed in the catch block. +SafeIntDefaultExceptionHandler - if you'd like to replace the exception handlers SafeInt provides, define your replacement and + define this. Note - two built in (Windows-specific) options exist: + - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an exception + - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught +SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator may create warnings, but if you'd like it to completely fail + to compile, define this. +SAFEINT_DISABLE_BINARY_ASSERT - binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do + this, the default is to assert. Set this if you prefer not to assert under these conditions. +SIZE_T_CAST_NEEDED - some compilers complain if there is not a cast to size_t, others complain if there is one. + This lets you not have your compiler complain. + +************************************************************************************************************************************/ + +/* +* The SafeInt class is designed to have as low an overhead as possible +* while still ensuring that all integer operations are conducted safely. +* Nearly every operator has been overloaded, with a very few exceptions. +* +* A usability-safety trade-off has been made to help ensure safety. This +* requires that every operation return either a SafeInt or a bool. If we +* allowed an operator to return a base integer type T, then the following +* can happen: +* +* char i = SafeInt(32) * 2 + SafeInt(16) * 4; +* +* The * operators take precedence, get overloaded, return a char, and then +* you have: +* +* char i = (char)64 + (char)64; //overflow! +* +* This situation would mean that safety would depend on usage, which isn't +* acceptable. +* +* One key operator that is missing is an implicit cast to type T. The reason for +* this is that if there is an implicit cast operator, then we end up with +* an ambiguous compile-time precedence. Because of this amiguity, there +* are two methods that are provided: +* +* Casting operators for every native integer type +* Version 3 note - it now compiles correctly for size_t without warnings +* +* SafeInt::Ptr() - returns the address of the internal integer +* Note - the '&' (address of) operator has been overloaded and returns +* the address of the internal integer. +* +* The SafeInt class should be used in any circumstances where ensuring +* integrity of the calculations is more important than performance. See Performance +* Notes below for additional information. +* +* Many of the conditionals will optimize out or be inlined for a release +* build (especially with /Ox), but it does have significantly more overhead, +* especially for signed numbers. If you do not _require_ negative numbers, use +* unsigned integer types - certain types of problems cannot occur, and this class +* performs most efficiently. +* +* Here's an example of when the class should ideally be used - +* +* void* AllocateMemForStructs(int StructSize, int HowMany) +* { +* SafeInt s(StructSize); +* +* s *= HowMany; +* +* return malloc(s); +* +* } +* +* Here's when it should NOT be used: +* +* void foo() +* { +* int i; +* +* for(i = 0; i < 0xffff; i++) +* .... +* } +* +* Error handling - a SafeInt class will throw exceptions if something +* objectionable happens. The exceptions are SafeIntException classes, +* which contain an enum as a code. +* +* Typical usage might be: +* +* bool foo() +* { +* SafeInt s; //note that s == 0 unless set +* +* try{ +* s *= 23; +* .... +* } +* catch(SafeIntException err) +* { +* //handle errors here +* } +* } +* +* Update for 3.0 - the exception class is now a template parameter. +* You can replace the exception class with any exception class you like. This is accomplished by: +* 1) Create a class that has the following interface: +* + template <> class YourSafeIntExceptionHandler < YourException > + { + public: + static __declspec(noreturn) void __stdcall SafeIntOnOverflow() + { + throw YourException( YourSafeIntArithmeticOverflowError ); + } + + static __declspec(noreturn) void __stdcall SafeIntOnDivZero() + { + throw YourException( YourSafeIntDivideByZeroError ); + } + }; +* +* Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do +* anything you like, just don't return from the call back into the code. +* +* 2) Either explicitly declare SafeInts like so: +* SafeInt< int, YourSafeIntExceptionHandler > si; +* or +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Performance: +* +* Due to the highly nested nature of this class, you can expect relatively poor +* performance in unoptimized code. In tests of optimized code vs. correct inline checks +* in native code, this class has been found to take approximately 8% more CPU time (this varies), +* most of which is due to exception handling. Solutions: +* +* 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1 +* (optimize for size) does not work as well. +* 2) If that 8% hit is really a serious problem, walk through the code and inline the +* exact same checks as the class uses. +* 3) Some operations are more difficult than others - avoid using signed integers, and if +* possible keep them all the same size. 64-bit integers are also expensive. Mixing +* different integer sizes and types may prove expensive. Be aware that literals are +* actually ints. For best performance, cast literals to the type desired. +* +* +* Performance update +* The current version of SafeInt uses template specialization to force the compiler to invoke only the +* operator implementation needed for any given pair of types. This will dramatically improve the perf +* of debug builds. +* +* 3.0 update - not only have we maintained the specialization, there were some cases that were overly complex, +* and using some additional cases (e.g. std::int64_t and std::uint64_t) resulted in some simplification. +* Additionally, there was a lot of work done to better optimize the 64-bit multiplication. +* +* Binary Operators +* +* All of the binary operators have certain assumptions built into the class design. +* This is to ensure correctness. Notes on each class of operator follow: +* +* Arithmetic Operators (*,/,+,-,%) +* There are three possible variants: +* SafeInt< T, E > op SafeInt< T, E > +* SafeInt< T, E > op U +* U op SafeInt< T, E > +* +* The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do +* this the compiler with throw the following error: +* +* error C2593: 'operator *' is ambiguous +* +* This is because the arithmetic operators are required to return a SafeInt of some type. +* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If +* you need to do this, you need to extract the value contained within one of the two using +* the casting operator. For example: +* +* SafeInt< T, E > t, result; +* SafeInt< U, E > u; +* +* result = t * (U)u; +* +* Comparison Operators +* Because each of these operators return type bool, mixing SafeInts of differing types is +* allowed. +* +* Shift Operators +* Shift operators always return the type on the left hand side of the operator. Mixed type +* operations are allowed because the return type is always known. +* +* Boolean Operators +* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts +* are allowed. Additionally, specific overloads exist for type bool on both sides of the +* operator. +* +* Binary Operators +* Mixed-type operations are discouraged, however some provision has been made in order to +* enable things like: +* +* SafeInt c = 2; +* +* if(c & 0x02) +* ... +* +* The "0x02" is actually an int, and it needs to work. +* In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner +* cases do exist where you could get unexpected results. In any case where SafeInt returns a different +* result than the underlying operator, it will call assert(). You should examine your code and cast things +* properly so that you are not programming with side effects. +* +* Documented issues: +* +* This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later. +* As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below. +* If you need a version that will work with lower level compilers, try version 1.0.7. None +* of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't +* been tried recently. +* +* It is strongly recommended that any code doing integer manipulation be compiled at /W4 +* - there are a number of warnings which pertain to integer manipulation enabled that are +* not enabled at /W3 (default for VC++) +* +* Perf note - postfix operators are slightly more costly than prefix operators. +* Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++ +* +* The comparison operator behavior in this class varies from the ANSI definition, which is +* arguably broken. As an example, consider the following: +* +* unsigned int l = 0xffffffff; +* char c = -1; +* +* if(c == l) +* printf("Why is -1 equal to 4 billion???\n"); +* +* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets +* cast again to an unsigned int, losing the true value. This behavior is despite the fact that +* an std::int64_t exists, and the following code will yield a different (and intuitively correct) +* answer: +* +* if((std::int64_t)c == (std::int64_t)l)) +* printf("Why is -1 equal to 4 billion???\n"); +* else +* printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); +* +* Note that combinations with smaller integers won't display the problem - if you +* changed "unsigned int" above to "unsigned short", you'd get the right answer. +* +* Revision history: +* +* Oct 12, 2003 - Created +* Author - David LeBlanc - dleblanc@microsoft.com +* +* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson +* Dec 28, 2003 - 1.0 +* added support for mixed-type operations +* thanks to vikramh +* also fixed broken std::int64_t multiplication section +* added extended support for mixed-type operations where possible +* Jan 28, 2004 - 1.0.1 +* changed WCHAR to wchar_t +* fixed a construct in two mixed-type assignment overloads that was +* not compiling on some compilers +* Also changed name of private method to comply with standards on +* reserved names +* Thanks to Niels Dekker for the input +* Feb 12, 2004 - 1.0.2 +* Minor changes to remove dependency on Windows headers +* Consistently used std::int16_t, std::int32_t and std::int64_t to ensure +* portability +* May 10, 2004 - 1.0.3 +* Corrected bug in one case of GreaterThan +* July 22, 2004 - 1.0.4 +* Tightened logic in addition check (saving 2 instructions) +* Pulled error handler out into function to enable user-defined replacement +* Made internal type of SafeIntException an enum (as per Niels' suggestion) +* Added casts for base integer types (as per Scott Meyers' suggestion) +* Updated usage information - see important new perf notes. +* Cleaned up several const issues (more thanks to Niels) +* +* Oct 1, 2004 - 1.0.5 +* Added support for SEH exceptions instead of C++ exceptions - Win32 only +* Made handlers for DIV0 and overflows individually overridable +* Commented out the destructor - major perf gains here +* Added cast operator for type long, since long != std::int32_t +* Corrected a couple of missing const modifiers +* Fixed broken >= and <= operators for type U op SafeInt< T, E > +* Nov 5, 2004 - 1.0.6 +* Implemented new logic in binary operators to resolve issues with +* implicit casts +* Fixed casting operator because char != signed char +* Defined std::int32_t as int instead of long +* Removed unsafe SafeInt::Value method +* Re-implemented casting operator as a result of removing Value method +* Dec 1, 2004 - 1.0.7 +* Implemented specialized operators for pointer arithmetic +* Created overloads for cases of U op= SafeInt. What you do with U +* after that may be dangerous. +* Fixed bug in corner case of MixedSizeModulus +* Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0 +* Added throw() decorations +* +* Apr 12, 2005 - 2.0 +* Extensive revisions to leverage template specialization. +* April, 2007 Extensive revisions for version 3.0 +* Nov 22, 2009 Forked from MS internal code +* Changes needed to support gcc compiler - many thanks to Niels Dekker +* for determining not just the issues, but also suggesting fixes. +* Also updating some of the header internals to be the same as the upcoming Visual Studio version. +* +* Jan 16, 2010 64-bit gcc has long == std::int64_t, which means that many of the existing 64-bit +* templates are over-specialized. This forces a redefinition of all the 64-bit +* multiplication routines to use pointers instead of references for return +* values. Also, let's use some intrinsics for x64 Microsoft compiler to +* reduce code size, and hopefully improve efficiency. +* +* June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported + compilers (Visual Studio, clang, gcc). + Also started to converge the code base such that the public CodePlex version will + be a drop-in replacement for the Visual Studio version. + +* Feb 12, 2018 Fixed floating point bug +* Fix to allow initialization by an enum +* Add support for static_assert, make it default to fix compiler warnings from C_ASSERT on gcc, clang +* Changed throw() to noexcept + +* March, 2018 Introduced support for constexpr, both the C++11 and C++14 flavors work. The C++14 standard + allows for much more thorough usage, and should be preferred. + +* Note about code style - throughout this class, casts will be written using C-style (T), +* not C++ style static_cast< T >. This is because the class is nearly always dealing with integer +* types, and in this case static_cast and a C cast are equivalent. Given the large number of casts, +* the code is a little more readable this way. In the event a cast is needed where static_cast couldn't +* be substituted, we'll use the new templatized cast to make it explicit what the operation is doing. +* +************************************************************************************************************ +* Version 3.0 changes: +* +* 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help +* those using well-developed exception classes. +* 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0. +* 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type, +* and you can cast it out (or assign) to a float as well. +* 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one. +* +* Another major improvement is the addition of external functions - if you just want to check an operation, this can now happen: +* All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially handy +* for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for 64-bit. +* +* inline bool SafeCast( const T From, U& To ) throw() +* inline bool SafeEquals( const T t, const U u ) throw() +* inline bool SafeNotEquals( const T t, const U u ) throw() +* inline bool SafeGreaterThan( const T t, const U u ) throw() +* inline bool SafeGreaterThanEquals( const T t, const U u ) throw() +* inline bool SafeLessThan( const T t, const U u ) throw() +* inline bool SafeLessThanEquals( const T t, const U u ) throw() +* inline bool SafeModulus( const T& t, const U& u, T& result ) throw() +* inline bool SafeMultiply( T t, U u, T& result ) throw() +* inline bool SafeDivide( T t, U u, T& result ) throw() +* inline bool SafeAdd( T t, U u, T& result ) throw() +* inline bool SafeSubtract( T t, U u, T& result ) throw() +* +*/ + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +namespace msl +{ + +namespace utilities +{ +#endif + +// catch these to handle errors +// Currently implemented code values: +// ERROR_ARITHMETIC_OVERFLOW +// EXCEPTION_INT_DIVIDE_BY_ZERO +enum SafeIntError +{ + SafeIntNoError = 0, + SafeIntArithmeticOverflow, + SafeIntDivideByZero +}; + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +} // utilities +} // msl +#endif + + +/* +* Error handler classes +* Using classes to deal with exceptions is going to allow the most +* flexibility, and we can mix different error handlers in the same project +* or even the same file. It isn't advisable to do this in the same function +* because a SafeInt< int, MyExceptionHandler > isn't the same thing as +* SafeInt< int, YourExceptionHander >. +* If for some reason you have to translate between the two, cast one of them back to its +* native type. +* +* To use your own exception class with SafeInt, first create your exception class, +* which may look something like the SafeIntException class below. The second step is to +* create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero. +* For example: +* +* template <> class SafeIntExceptionHandler < YourExceptionClass > +* { +* static __declspec(noreturn) void __stdcall SafeIntOnOverflow() +* { +* throw YourExceptionClass( EXCEPTION_INT_OVERFLOW ); +* } +* +* static __declspec(noreturn) void __stdcall SafeIntOnDivZero() +* { +* throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO ); +* } +* }; +* +* typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler +* You'd then declare your SafeInt objects like this: +* SafeInt< int, YourSafeIntExceptionHandler > +* +* Unfortunately, there is no such thing as partial template specialization in typedef +* statements, so you have three options if you find this cumbersome: +* +* 1) Create a holder class: +* +* template < typename T > +* class MySafeInt +* { +* public: +* SafeInt< T, MyExceptionClass> si; +* }; +* +* You'd then declare an instance like so: +* MySafeInt< int > i; +* +* You'd lose handy things like initialization - it would have to be initialized as: +* +* i.si = 0; +* +* 2) You could create a typedef for every int type you deal with: +* +* typedef SafeInt< int, MyExceptionClass > MySafeInt; +* typedef SafeInt< char, MyExceptionClass > MySafeChar; +* +* and so on. The second approach is probably more usable, and will just drop into code +* better, which is the original intent of the SafeInt class. +* +* 3) If you're going to consistently use a different class to handle your exceptions, +* you can override the default typedef like so: +* +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Overall, this is probably the best approach. +* */ + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +namespace msl +{ + +namespace utilities +{ +#endif + +#if defined SAFEINT_ASSERT_ON_EXCEPTION + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } +#else + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} +#endif + +// Note - removed weak annotation on class due to gcc complaints +// This was the only place in the file that used it, need to better understand +// whether it was put there correctly in the first place + +class SAFEINT_VISIBLE SafeIntException +{ +public: + _CONSTEXPR11 SafeIntException( SafeIntError code = SafeIntNoError) SAFEINT_NOTHROW : m_code(code) + { + } + SafeIntError m_code; +}; + +namespace SafeIntInternal +{ + // Visual Studio version of SafeInt provides for two possible error + // handlers: + // SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined + // SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers, + // exits the app with a crash + template < typename E > class SafeIntExceptionHandler; + + template <> class SafeIntExceptionHandler < SafeIntException > + { + public: + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntArithmeticOverflow ); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntDivideByZero ); + } + }; + + class SafeInt_InvalidParameter + { + public: + static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); + } + + static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); + } + }; + +#if defined _WINDOWS_ + + class SafeIntWin32ExceptionHandler + { + public: + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0); + } + }; + +#endif + +} // namespace SafeIntInternal + +// both of these have cross-platform support +typedef SafeIntInternal::SafeIntExceptionHandler < SafeIntException > CPlusPlusExceptionHandler; +typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler; + +// This exception handler is no longer recommended, but is left here in order not to break existing users +#if defined _WINDOWS_ +typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; +#endif + +// For Visual Studio compatibility +#if defined VISUAL_STUDIO_SAFEINT_COMPAT + typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException; + typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter; +#endif + +// If the user hasn't defined a default exception handler, +// define one now, depending on whether they would like Win32 or C++ exceptions + +// This library will use conditional noexcept soon, but not in this release +// Some users might mix exception handlers, which is not advised, but is supported +#if !defined SafeIntDefaultExceptionHandler + #if defined SAFEINT_RAISE_EXCEPTION + #if !defined _WINDOWS_ + #error Include windows.h in order to use Win32 exceptions + #endif + + #define SafeIntDefaultExceptionHandler Win32ExceptionHandler + #elif defined SAFEINT_FAILFAST + #define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler + #else + #define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler + #if !defined SAFEINT_EXCEPTION_HANDLER_CPP + #define SAFEINT_EXCEPTION_HANDLER_CPP 1 + #endif + #endif +#endif + +#if !defined SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_EXCEPTION_HANDLER_CPP 0 +#endif + +// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast, +// or abort, then all methods become no throw. Some teams track throw() annotations closely, +// and the following option provides for this. +#if SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_CPP_THROW +#else +#define SAFEINT_CPP_THROW SAFEINT_NOTHROW +#endif + +namespace safeint_internal +{ + // If we have support for std, then we can do this easily, and detect enums as well + template < typename T > class numeric_type; + + // Continue to special case bool + template <> class numeric_type { public: enum { isBool = true, isInt = false }; }; + template < typename T > class numeric_type + { + public: + enum + { + isBool = false, // We specialized out a bool + // If it is an enum, then consider it an int type + // This does allow someone to make a SafeInt from an enum type, which is not recommended, + // but it also allows someone to add an enum value to a SafeInt, which is handy. + isInt = std::is_integral::value || std::is_enum::value, + isEnum = std::is_enum::value + }; + }; + + template < typename T > class int_traits + { + public: + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + + enum + { + is64Bit = (sizeof(T) == 8), + is32Bit = (sizeof(T) == 4), + is16Bit = (sizeof(T) == 2), + is8Bit = (sizeof(T) == 1), + isLT32Bit = (sizeof(T) < 4), + isLT64Bit = (sizeof(T) < 8), + isInt8 = (sizeof(T) == 1 && std::numeric_limits::is_signed), + isUint8 = (sizeof(T) == 1 && !std::numeric_limits::is_signed), + isInt16 = (sizeof(T) == 2 && std::numeric_limits::is_signed), + isUint16 = (sizeof(T) == 2 && !std::numeric_limits::is_signed), + isInt32 = (sizeof(T) == 4 && std::numeric_limits::is_signed), + isUint32 = (sizeof(T) == 4 && !std::numeric_limits::is_signed), + isInt64 = (sizeof(T) == 8 && std::numeric_limits::is_signed), + isUint64 = (sizeof(T) == 8 && !std::numeric_limits::is_signed), + bitCount = (sizeof(T) * 8), + isBool = ((T)2 == (T)1) + }; + }; + + template < typename T, typename U > class type_compare + { + public: + enum + { + isBothSigned = (std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed), + isBothUnsigned = (!std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed), + isLikeSigned = ((bool)(std::numeric_limits< T >::is_signed) == (bool)(std::numeric_limits< U >::is_signed)), + isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || + (std::numeric_limits< T >::is_signed && sizeof(T) > sizeof(U))), + isBothLT32Bit = (safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::isLT32Bit), + isBothLT64Bit = (safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isLT64Bit) + }; + }; + +} +//all of the arithmetic operators can be solved by the same code within +//each of these regions without resorting to compile-time constant conditionals +//most operators collapse the problem into less than the 22 zones, but this is used +//as the first cut +//using this also helps ensure that we handle all of the possible cases correctly + +template < typename T, typename U > class IntRegion +{ +public: + enum + { + //unsigned-unsigned zone + IntZone_UintLT32_UintLT32 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::type_compare< T,U >::isBothLT32Bit, + IntZone_Uint32_UintLT64 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::is32Bit && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_UintLT32_Uint32 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::is32Bit, + IntZone_Uint64_Uint = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::is64Bit, + IntZone_UintLT64_Uint64 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::is64Bit, + //unsigned-signed + IntZone_UintLT32_IntLT32 = !std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed && safeint_internal::type_compare< T,U >::isBothLT32Bit, + IntZone_Uint32_IntLT64 = safeint_internal::int_traits< T >::isUint32 && std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_UintLT32_Int32 = !std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::isInt32, + IntZone_Uint64_Int = safeint_internal::int_traits< T >::isUint64 && std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_UintLT64_Int64 = !std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isInt64, + IntZone_Uint64_Int64 = safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64, + //signed-signed + IntZone_IntLT32_IntLT32 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::type_compare< T, U >::isBothLT32Bit, + IntZone_Int32_IntLT64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::is32Bit && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_IntLT32_Int32 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::is32Bit, + IntZone_Int64_Int64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isInt64, + IntZone_Int64_Int = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::is64Bit && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_IntLT64_Int64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::is64Bit, + //signed-unsigned + IntZone_IntLT32_UintLT32 = std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed && safeint_internal::type_compare< T,U >::isBothLT32Bit, + IntZone_Int32_UintLT32 = safeint_internal::int_traits< T >::isInt32 && !std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT32Bit, + IntZone_IntLT64_Uint32 = std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isUint32, + IntZone_Int64_UintLT64 = safeint_internal::int_traits< T >::isInt64 && !std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_Int_Uint64 = std::numeric_limits< T >::is_signed && safeint_internal::int_traits< U >::isUint64 && safeint_internal::int_traits< T >::isLT64Bit, + IntZone_Int64_Uint64 = safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64 + }; +}; + +// In all of the following functions, we have two versions +// One for SafeInt, which throws C++ (or possibly SEH) exceptions +// The non-throwing versions are for use by the helper functions that return success and failure. +// Some of the non-throwing functions are not used, but are maintained for completeness. + +// There's no real alternative to duplicating logic, but keeping the two versions +// immediately next to one another will help reduce problems + +// useful function to help with getting the magnitude of a negative number +enum AbsMethod +{ + AbsMethodInt, + AbsMethodInt64, + AbsMethodNoop +}; + +template < typename T > +class GetAbsMethod +{ +public: + enum + { + method = safeint_internal::int_traits< T >::isLT64Bit && std::numeric_limits< T >::is_signed ? AbsMethodInt : + safeint_internal::int_traits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop + }; +}; + +// let's go ahead and hard-code a dependency on the +// representation of negative numbers to keep compilers from getting overly +// happy with optimizing away things like -MIN_INT. +template < typename T, int > class AbsValueHelper; + +template < typename T > class AbsValueHelper < T, AbsMethodInt> +{ +public: + _CONSTEXPR14 static std::uint32_t Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(std::uint32_t)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodInt64 > +{ +public: + _CONSTEXPR14 static std::uint64_t Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(std::uint64_t)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodNoop > +{ +public: + _CONSTEXPR14 static T Abs( T t ) SAFEINT_NOTHROW + { + // Why are you calling Abs on an unsigned number ??? + SAFEINT_ASSERT( false ); + return t; + } +}; + +// Helper classes to work keep compilers from +// optimizing away negation +template < typename T > class SignedNegation; + +template <> +class SignedNegation +{ +public: + _CONSTEXPR11 static std::int32_t Value(std::uint64_t in) SAFEINT_NOTHROW + { + return (std::int32_t)(~(std::uint32_t)in + 1); + } + + _CONSTEXPR11 static std::int32_t Value(std::uint32_t in) SAFEINT_NOTHROW + { + return (std::int32_t)(~in + 1); + } +}; + +template <> +class SignedNegation +{ +public: + _CONSTEXPR11 static std::int64_t Value(std::uint64_t in) SAFEINT_NOTHROW + { + return (std::int64_t)(~in + 1); + } +}; + +template < typename T, bool > class NegationHelper; +// Previous versions had an assert that the type being negated was 32-bit or higher +// In retrospect, this seems like something to just document +// Negation will normally upcast to int +// For example -(unsigned short)0xffff == (int)0xffff0001 +// This class will retain the type, and will truncate, which may not be what +// you wanted +// If you want normal operator casting behavior, do this: +// SafeInt ss = 0xffff; +// then: +// -(SafeInt(ss)) +// will then emit a signed int with the correct value and bitfield + +// Note, unlike all of the other helper classes, the non-throwing negation +// doesn't make sense, isn't exposed or tested, so omit it + +template < typename T > class NegationHelper // Signed +{ +public: + template + _CONSTEXPR14 static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { + // corner case + if( t != std::numeric_limits::min() ) + { + // cast prevents unneeded checks in the case of small ints + return -t; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T > class NegationHelper // unsigned +{ +public: + template + _CONSTEXPR14 static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { +#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION + static_assert( sizeof(T) == 0, "Unsigned negation is unsupported" ); +#endif + // This may not be the most efficient approach, but you shouldn't be doing this + return (T)SignedNegation::Value(t); + } + +}; + +//core logic to determine casting behavior +enum CastMethod +{ + CastOK = 0, + CastCheckLTZero, + CastCheckGTMax, + CastCheckSafeIntMinMaxUnsigned, + CastCheckSafeIntMinMaxSigned, + CastToFloat, + CastFromFloat, + CastToBool, + CastFromBool, + CastFromEnum +}; + + +template < typename ToType, typename FromType > +class GetCastMethod +{ +public: + enum + { + method = ( safeint_internal::numeric_type::isEnum ) ? CastFromEnum : + ( safeint_internal::int_traits< FromType >::isBool && + !safeint_internal::int_traits< ToType >::isBool ) ? CastFromBool : + + ( !safeint_internal::int_traits< FromType >::isBool && + safeint_internal::int_traits< ToType >::isBool ) ? CastToBool : + + ( safeint_internal::type_compare< ToType, FromType >::isCastOK ) ? CastOK : + + ( ( std::numeric_limits< ToType >::is_signed && + !std::numeric_limits< FromType >::is_signed && + sizeof( FromType ) >= sizeof( ToType ) ) || + ( safeint_internal::type_compare< ToType, FromType >::isBothUnsigned && + sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : + + ( !std::numeric_limits< ToType >::is_signed && + std::numeric_limits< FromType >::is_signed && + sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : + + ( !std::numeric_limits< ToType >::is_signed ) ? CastCheckSafeIntMinMaxUnsigned + : CastCheckSafeIntMinMaxSigned + }; +}; + +template < typename FromType > class GetCastMethod < float, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < long double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename ToType > class GetCastMethod < ToType, float > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, long double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename T, typename U, int > class SafeCastHelper; + +template < typename T, typename U > class SafeCastHelper < T, U, CastOK > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + t = (T)u; + } +}; + +template class float_cast_helper; + +template class float_cast_helper // Unsigned case +{ +public: + _CONSTEXPR14 static bool Test(double d) + { + const std::uint64_t signifDouble = 0x1fffffffffffff; + + // Anything larger than this either is larger than 2^64-1, or cannot be represented by a double + const std::uint64_t maxUnsignedDouble = signifDouble << 11; + + // There is the possibility of both negative and positive zero, + // but we'll allow either, since (-0.0 < 0) == false + // if we wanted to change that, then use the signbit() macro + if (d < 0 || d > static_cast(maxUnsignedDouble)) + return false; + + // The input can now safely be cast to an unsigned long long + if (static_cast(d) > std::numeric_limits::max()) + return false; + + return true; + } +}; + +template class float_cast_helper // Signed case +{ +public: + _CONSTEXPR14 static bool Test(double d) + { + const std::uint64_t signifDouble = 0x1fffffffffffff; + // This has to fit in 2^63-1 + const std::uint64_t maxSignedDouble = signifDouble << 10; + // The smallest signed long long is easier + const std::int64_t minSignedDouble = static_cast(0x8000000000000000); + + if (d < static_cast(minSignedDouble) || d > static_cast(maxSignedDouble)) + return false; + + // And now cast to long long, and check against min and max for this type + std::int64_t test = static_cast(d); + if ((std::int64_t)test < (std::int64_t)std::numeric_limits::min() || (std::int64_t)test >(std::int64_t)std::numeric_limits::max()) + return false; + + return true; + } +}; + +// special case floats and doubles +template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > +{ +public: + + static bool CheckFloatingPointCast(double d) + { + // A double can hold at most 53 bits of the value + // 53 bits is: + bool fValid = false; + + switch (std::fpclassify(d)) + { + case FP_NORMAL: // A positive or negative normalized non - zero value + case FP_SUBNORMAL: // A positive or negative denormalized value + case FP_ZERO: // A positive or negative zero value + fValid = true; + break; + + case FP_NAN: // A quiet, signaling, or indeterminate NaN + case FP_INFINITE: // A positive or negative infinity + default: + fValid = false; + break; + } + + if (!fValid) + return false; + + return float_cast_helper< T, !std::numeric_limits< T >::is_signed >::Test(d); + } + + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if(CheckFloatingPointCast(u)) + { + t = (T)u; + return true; + } + return false; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if (CheckFloatingPointCast(u)) + { + t = (T)u; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastFromEnum > +{ +public: + _CONSTEXPR14 static bool Cast(U u, T& t) SAFEINT_NOTHROW + { + return SafeCastHelper< T, int, GetCastMethod< T, int >::method >::Cast(static_cast(u), t); + } + + template < typename E > + _CONSTEXPR14 static void CastThrow(U u, T& t) SAFEINT_CPP_THROW + { + SafeCastHelper< T, int, GetCastMethod< T, int >::method >::template CastThrow< E >(static_cast(u), t); + } +}; + +// Match on any method where a bool is cast to type T +template < typename T > class SafeCastHelper < T, bool, CastFromBool > +{ +public: + _CONSTEXPR14 static bool Cast( bool b, T& t ) SAFEINT_NOTHROW + { + t = (T)( b ? 1 : 0 ); + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + t = (T)( b ? 1 : 0 ); + } +}; + +template < typename T > class SafeCastHelper < bool, T, CastToBool > +{ +public: + _CONSTEXPR14 static bool Cast( T t, bool& b ) SAFEINT_NOTHROW + { + b = !!t; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + b = !!t; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckLTZero > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckGTMax > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u > (U)std::numeric_limits::max() ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u > (U)std::numeric_limits::max() ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxUnsigned > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // U is signed - T could be either signed or unsigned + if( u > std::numeric_limits::max() || u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + // U is signed - T could be either signed or unsigned + if( u > std::numeric_limits::max() || u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxSigned > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // T, U are signed + if( u > std::numeric_limits::max() || u < std::numeric_limits::min() ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + //T, U are signed + if( u > std::numeric_limits::max() || u < std::numeric_limits::min() ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +//core logic to determine whether a comparison is valid, or needs special treatment +enum ComparisonMethod +{ + ComparisonMethod_Ok = 0, + ComparisonMethod_CastInt, + ComparisonMethod_CastInt64, + ComparisonMethod_UnsignedT, + ComparisonMethod_UnsignedU +}; + + // Note - the standard is arguably broken in the case of some integer + // conversion operations + // For example, signed char a = -1 = 0xff + // unsigned int b = 0xffffffff + // If you then test if a < b, a value-preserving cast + // is made, and you're essentially testing + // (unsigned int)a < b == false + // + // I do not think this makes sense - if you perform + // a cast to an std::int64_t, which can clearly preserve both value and signedness + // then you get a different and intuitively correct answer + // IMHO, -1 should be less than 4 billion + // If you prefer to retain the ANSI standard behavior + // insert #define ANSI_CONVERSIONS into your source + // Behavior differences occur in the following cases: + // 8, 16, and 32-bit signed int, unsigned 32-bit int + // any signed int, unsigned 64-bit int + // Note - the signed int must be negative to show the problem + +template < typename T, typename U > +class ValidComparison +{ +public: + enum + { + method = ( ( safeint_internal::type_compare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok : + ( ( std::numeric_limits< T >::is_signed && sizeof(T) < 8 && sizeof(U) < 4 ) || + ( std::numeric_limits< U >::is_signed && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt : + ( ( std::numeric_limits< T >::is_signed && sizeof(U) < 8 ) || + ( std::numeric_limits< U >::is_signed && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 : + ( !std::numeric_limits< T >::is_signed ) ? ComparisonMethod_UnsignedT : + ComparisonMethod_UnsignedU ) + }; +}; + +template class EqualityTest; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > +{ +public: + _CONSTEXPR11 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( t == u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt > +{ +public: + _CONSTEXPR11 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t == (int)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + _CONSTEXPR11 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (std::int64_t)t == (std::int64_t)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + _CONSTEXPR14 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return false; + + //else safe to cast to type T + return ( t == (T)u ); + } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> +{ +public: + _CONSTEXPR14 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + //else safe to cast to type U + return ( (U)t == u ); + } +}; + +template class GreaterThanTest; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > +{ +public: + _CONSTEXPR11 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( t > u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt > +{ +public: + _CONSTEXPR11 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t > (int)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + _CONSTEXPR11 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (std::int64_t)t > (std::int64_t)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + _CONSTEXPR14 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return true; + + // else safe to cast to type T + return ( t > (T)u ); + } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > +{ +public: + _CONSTEXPR14 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + // else safe to cast to type U + return ( (U)t > u ); + } +}; + +// Modulus is simpler than comparison, but follows much the same logic +// using this set of functions, it can't fail except in a div 0 situation +template class ModulusHelper; + +template class mod_corner_case; + +template class mod_corner_case // signed +{ +public: + _CONSTEXPR14 static bool is_undefined(U u) + { + return (u == -1); + } +}; + +template class mod_corner_case // unsigned +{ +public: + _CONSTEXPR14 static bool is_undefined(U) + { + return false; + } +}; + +template class ModulusHelper +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if(mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return SafeIntNoError; + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return; + } + + result = (T)(t % u); + } +}; + +template class ModulusHelper +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return SafeIntNoError; + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return; + } + + result = (T)(t % u); + } +}; + +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_CastInt64> +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return SafeIntNoError; + } + + result = (T)((std::int64_t)t % (std::int64_t)u); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return; + } + + result = (T)((std::int64_t)t % (std::int64_t)u); + } +}; + +// T is std::uint64_t, U is any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedT> +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + // u could be negative - if so, need to convert to positive + // casts below are always safe due to the way modulus works + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs(u)); + else + result = (T)(t % u); + + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + // u could be negative - if so, need to convert to positive + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u )); + else + result = (T)(t % u); + } +}; + +// U is std::uint64_t, T any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedU> +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1 ); + else + result = (T)((T)t % u); + + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1); + else + result = (T)( (T)t % u ); + } +}; + +//core logic to determine method to check multiplication +enum MultiplicationState +{ + MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit + MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit + MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit + MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller + MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller + MultiplicationState_Uint64Uint64, // Both are unsigned int64 + MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 + MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 + MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit + MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 + MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 + MultiplicationState_Int64Int64, // lhs int64, rhs int64 + MultiplicationState_Int64Int, // lhs int64, rhs int32 + MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 + MultiplicationState_IntInt64, // lhs int, rhs int64 + MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 + MultiplicationState_Error +}; + +template < typename T, typename U > +class MultiplicationMethod +{ +public: + enum + { + // unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint : + (IntRegion< T,U >::IntZone_Uint32_UintLT64 || + IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 : + safeint_internal::type_compare< T,U >::isBothUnsigned && + safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isUint64 ? MultiplicationState_Uint64Uint64 : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 : + (IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 : + MultiplicationState_Error ) ) + }; +}; + +template class MultiplicationHelper; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt> +{ +public: + //accepts signed, both less than 32-bit + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + int tmp = t * u; + + if( tmp > std::numeric_limits::max() || tmp < std::numeric_limits::min() ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + int tmp = t * u; + + if( tmp > std::numeric_limits::max() || tmp < std::numeric_limits::min() ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint > +{ +public: + //accepts unsigned, both less than 32-bit + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + unsigned int tmp = (unsigned int)(t * u); + + if( tmp > std::numeric_limits::max() ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + unsigned int tmp = (unsigned int)( t * u ); + + if( tmp > std::numeric_limits::max() ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt64> +{ +public: + //mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + std::int64_t tmp = (std::int64_t)t * (std::int64_t)u; + + if(tmp > (std::int64_t)std::numeric_limits::max() || tmp < (std::int64_t)std::numeric_limits::min()) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + std::int64_t tmp = (std::int64_t)t * (std::int64_t)u; + + if(tmp > (std::int64_t)std::numeric_limits::max() || tmp < (std::int64_t)std::numeric_limits::min()) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint64> +{ +public: + //both unsigned where at least one argument is 32-bit, and both are 32-bit or less + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + std::uint64_t tmp = (std::uint64_t)t * (std::uint64_t)u; + + if(tmp > (std::uint64_t)std::numeric_limits::max()) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + std::uint64_t tmp = (std::uint64_t)t * (std::uint64_t)u; + + if(tmp > (std::uint64_t)std::numeric_limits::max()) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +// T = left arg and return type +// U = right arg +template < typename T, typename U > class LargeIntRegMultiply; + +#if SAFEINT_USE_INTRINSICS +// As usual, unsigned is easy +inline bool IntrinsicMultiplyUint64( const std::uint64_t& a, const std::uint64_t& b, std::uint64_t* pRet ) SAFEINT_NOTHROW +{ + std::uint64_t ulHigh = 0; + *pRet = _umul128(a , b, &ulHigh); + return ulHigh == 0; +} + +// Signed, is not so easy +inline bool IntrinsicMultiplyInt64( const std::int64_t& a, const std::int64_t& b, std::int64_t* pRet ) SAFEINT_NOTHROW +{ + std::int64_t llHigh = 0; + *pRet = _mul128(a , b, &llHigh); + + // Now we need to figure out what we expect + // If llHigh is 0, then treat *pRet as unsigned + // If llHigh is < 0, then treat *pRet as signed + + if( (a ^ b) < 0 ) + { + // Negative result expected + if( llHigh == -1 && *pRet < 0 || + llHigh == 0 && *pRet == 0 ) + { + // Everything is within range + return true; + } + } + else + { + // Result should be positive + // Check for overflow + if( llHigh == 0 && (std::uint64_t)*pRet <= (std::uint64_t)std::numeric_limits::max() ) + return true; + } + return false; +} + +#endif + +template<> class LargeIntRegMultiply< std::uint64_t, std::uint64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, const std::uint64_t& b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, b, pRet ); +#else + std::uint32_t aHigh = 0, aLow = 0, bHigh = 0, bLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (std::uint64_t)aLow * (std::uint64_t)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)bLow; + } + } + else + { + return false; + } + + if(*pRet != 0) + { + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)bLow; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)bLow; + return true; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, const std::uint64_t& b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + std::uint32_t aHigh = 0, aLow = 0, bHigh = 0, bLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (std::uint64_t)aLow * (std::uint64_t)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)bLow; + } + } + else + { + E::SafeIntOnOverflow(); + } + + if(*pRet != 0) + { + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)bLow; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)bLow; +#endif + } +}; + +template<> class LargeIntRegMultiply< std::uint64_t, std::uint32_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, std::uint32_t b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ); +#else + std::uint32_t aHigh = 0, aLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)b; + + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)b; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)b; + return true; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, std::uint32_t b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + std::uint32_t aHigh = 0, aLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)b; + + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)b; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)b; + return; +#endif + } +}; + +template<> class LargeIntRegMultiply< std::uint64_t, std::int32_t > +{ +public: + // Intrinsic not needed + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, std::int32_t b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ); +#else + return LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply(a, (std::uint32_t)b, pRet); +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, std::int32_t b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( a, (std::uint32_t)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::uint64_t, std::int64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, std::int64_t b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ); +#else + return LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply(a, (std::uint64_t)b, pRet); +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, std::int64_t b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< std::uint64_t, std::uint64_t >::template RegMultiplyThrow< E >( a, (std::uint64_t)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int32_t, std::uint64_t > +{ +public: + // Devolves into ordinary 64-bit calculation + _CONSTEXPR14 static bool RegMultiply( std::int32_t a, const std::uint64_t& b, std::int32_t* pRet ) SAFEINT_NOTHROW + { + std::uint32_t bHigh = 0, bLow = 0; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // aHigh == 0 implies: + // ( aLow * bHigh * 2^32 ) + ( aLow + bLow ) + // If the first part is != 0, fail + + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + return false; + + if( a < 0 ) + { + + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + fIsNegative = true; + } + + std::uint64_t tmp = (std::uint32_t)a * (std::uint64_t)bLow; + + if( !fIsNegative ) + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return true; + } + } + else + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max()+1 ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void RegMultiplyThrow( std::int32_t a, const std::uint64_t& b, std::int32_t* pRet ) SAFEINT_CPP_THROW + { + std::uint32_t bHigh = 0, bLow = 0; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + E::SafeIntOnOverflow(); + + if( a < 0 ) + { + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + fIsNegative = true; + } + + std::uint64_t tmp = (std::uint32_t)a * (std::uint64_t)bLow; + + if( !fIsNegative ) + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return; + } + } + else + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max()+1 ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template<> class LargeIntRegMultiply< std::uint32_t, std::uint64_t > +{ +public: + // Becomes ordinary 64-bit multiplication, intrinsic not needed + _CONSTEXPR14 static bool RegMultiply( std::uint32_t a, const std::uint64_t& b, std::uint32_t* pRet ) SAFEINT_NOTHROW + { + // Consider that a*b can be broken up into: + // (bHigh * 2^32 + bLow) * a + // => (bHigh * a * 2^32) + (bLow * a) + // In this case, the result must fit into 32-bits + // If bHigh != 0 && a != 0, immediate error. + + if( (std::uint32_t)(b >> 32) != 0 && a != 0 ) + return false; + + std::uint64_t tmp = b * (std::uint64_t)a; + + if( (std::uint32_t)(tmp >> 32) != 0 ) // overflow + return false; + + *pRet = (std::uint32_t)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void RegMultiplyThrow( std::uint32_t a, const std::uint64_t& b, std::uint32_t* pRet ) SAFEINT_CPP_THROW + { + if( (std::uint32_t)(b >> 32) != 0 && a != 0 ) + E::SafeIntOnOverflow(); + + std::uint64_t tmp = b * (std::uint64_t)a; + + if( (std::uint32_t)(tmp >> 32) != 0 ) // overflow + E::SafeIntOnOverflow(); + + *pRet = (std::uint32_t)tmp; + } +}; + +template<> class LargeIntRegMultiply< std::uint32_t, std::int64_t > +{ +public: + _CONSTEXPR14 static bool RegMultiply( std::uint32_t a, const std::int64_t& b, std::uint32_t* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + return LargeIntRegMultiply< std::uint32_t, std::uint64_t >::RegMultiply( a, (std::uint64_t)b, pRet ); + } + + template < typename E > + _CONSTEXPR14 static void RegMultiplyThrow( std::uint32_t a, const std::int64_t& b, std::uint32_t* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + + LargeIntRegMultiply< std::uint32_t, std::uint64_t >::template RegMultiplyThrow< E >( a, (std::uint64_t)b, pRet ); + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::int64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, const std::int64_t& b, std::int64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + std::int64_t b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( (std::uint64_t)a1, (std::uint64_t)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::int64_t& a, const std::int64_t& b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + std::int64_t b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + LargeIntRegMultiply< std::uint64_t, std::uint64_t >::template RegMultiplyThrow< E >( (std::uint64_t)a1, (std::uint64_t)b1, &tmp ); + + // The unsigned multiplication didn't overflow or we'd be in the exception handler + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::uint32_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, std::uint32_t b, std::int64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ); +#else + bool aNegative = false; + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply( (std::uint64_t)a1, b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::int64_t& a, std::uint32_t b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( (std::uint64_t)a1, b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::int32_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, std::int32_t b, std::int64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + std::int64_t b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply( (std::uint64_t)a1, (std::uint32_t)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( std::int64_t a, std::int32_t b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + + if( a < 0 ) + { + aNegative = true; + a = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(b); + } + + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( (std::uint64_t)a, (std::uint32_t)b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int32_t, std::int64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( std::int32_t a, const std::int64_t& b, std::int32_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + std::int64_t tmp = 0; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > std::numeric_limits< std::int32_t >::max() || + tmp < std::numeric_limits< std::int32_t >::min() ) + { + return false; + } + + *pRet = (std::int32_t)tmp; + return true; + } + return false; +#else + bool aNegative = false; + bool bNegative = false; + + std::uint32_t tmp = 0; + std::int64_t b1 = b; + + if( a < 0 ) + { + aNegative = true; + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< std::uint32_t, std::uint64_t >::RegMultiply( (std::uint32_t)a, (std::uint64_t)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::min() ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( std::int32_t a, const std::int64_t& b, std::int32_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + std::int64_t tmp; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > std::numeric_limits< std::int32_t >::max() || + tmp < std::numeric_limits< std::int32_t >::min() ) + { + E::SafeIntOnOverflow(); + } + + *pRet = (std::int32_t)tmp; + return; + } + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint32_t tmp = 0; + std::int64_t b2 = b; + + if( a < 0 ) + { + aNegative = true; + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b2 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b2); + } + + LargeIntRegMultiply< std::uint32_t, std::uint64_t >::template RegMultiplyThrow< E >( (std::uint32_t)a, (std::uint64_t)b2, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::min() ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::uint64_t > +{ +public: + // Leave this one as-is - will call unsigned intrinsic internally + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, const std::uint64_t& b, std::int64_t* pRet ) SAFEINT_NOTHROW + { + bool aNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( (std::uint64_t)a1, (std::uint64_t)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::int64_t& a, const std::uint64_t& b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { + bool aNegative = false; + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( (std::uint64_t)a1, (std::uint64_t)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +// In all of the following functions where LargeIntRegMultiply methods are called, +// we need to properly transition types. The methods need std::int64_t, std::int32_t, etc. +// but the variables being passed to us could be long long, long int, or long, depending on +// the compiler. Microsoft compiler knows that long long is the same type as std::int64_t, but gcc doesn't + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint64 > +{ +public: + // T, U are std::uint64_t + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isUint64, "T, U must be Uint64" ); + std::uint64_t t1 = t; + std::uint64_t u1 = u; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( t1, u1, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow(const std::uint64_t& t, const std::uint64_t& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isUint64, "T, U must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t u1 = u; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::uint64_t >::template RegMultiplyThrow< E >( t1, u1, &tmp ); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint > +{ +public: + // T is std::uint64_t + // U is any unsigned int 32-bit or less + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isUint64, "T must be Uint64" ); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "T must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + } +}; + +// converse of the previous function +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintUint64 > +{ +public: + // T is any unsigned int up to 32-bit + // U is std::uint64_t + _CONSTEXPR14 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::uint32_t tmp = 0; + + if( LargeIntRegMultiply< std::uint32_t, std::uint64_t >::RegMultiply( t, u1, &tmp ) && + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::uint32_t tmp = 0; + + LargeIntRegMultiply< std::uint32_t, std::uint64_t >::template RegMultiplyThrow< E >( t, u1, &tmp ); + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int > +{ +public: + // T is std::uint64_t + // U is any signed int, up to 64-bit + _CONSTEXPR14_MULTIPLY static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "T must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::int32_t >::RegMultiply(t1, (std::int32_t)u, &tmp); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "T must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::int32_t >::template RegMultiplyThrow< E >(t1, (std::int32_t)u, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int64 > +{ +public: + // T is std::uint64_t + // U is std::int64_t + _CONSTEXPR14_MULTIPLY static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isInt64, "T must be Uint64, U Int64" ); + std::uint64_t t1 = t; + std::int64_t u1 = u; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::int64_t >::RegMultiply(t1, u1, &tmp); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isInt64, "T must be Uint64, U Int64"); + std::uint64_t t1 = t; + std::int64_t u1 = u; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::int64_t >::template RegMultiplyThrow< E >(t1, u1, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintInt64 > +{ +public: + // T is unsigned up to 32-bit + // U is std::int64_t + _CONSTEXPR14 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isInt64, "U must be Int64"); + std::int64_t u1 = u; + std::uint32_t tmp = 0; + + if( LargeIntRegMultiply< std::uint32_t, std::int64_t >::RegMultiply( (std::uint32_t)t, u1, &tmp ) && + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "U must be Int64"); + std::int64_t u1 = u; + std::uint32_t tmp = 0; + + LargeIntRegMultiply< std::uint32_t, std::int64_t >::template RegMultiplyThrow< E >( (std::uint32_t)t, u1, &tmp ); + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint > +{ +public: + // T is std::int64_t + // U is unsigned up to 32-bit + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::uint32_t >::RegMultiply( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::uint32_t >::template RegMultiplyThrow< E >( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int64 > +{ +public: + // T, U are std::int64_t + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isInt64, "T, U must be Int64" ); + std::int64_t t1 = t; + std::int64_t u1 = u; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::int64_t >::RegMultiply( t1, u1, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isInt64, "T, U must be Int64"); + std::int64_t t1 = t; + std::int64_t u1 = u; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::int64_t >::template RegMultiplyThrow< E >( t1, u1, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int > +{ +public: + // T is std::int64_t + // U is signed up to 32-bit + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, U u, T& ret ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::int32_t >::RegMultiply( t1, (std::int32_t)u, &tmp); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const std::int64_t& t, U u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::int32_t >::template RegMultiplyThrow< E >(t1, (std::int32_t)u, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntUint64 > +{ +public: + // T is signed up to 32-bit + // U is std::uint64_t + _CONSTEXPR14 static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::int32_t tmp = 0; + + if( LargeIntRegMultiply< std::int32_t, std::uint64_t >::RegMultiply( (std::int32_t)t, u1, &tmp ) && + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(T t, const std::uint64_t& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::int32_t tmp = 0; + + LargeIntRegMultiply< std::int32_t, std::uint64_t >::template RegMultiplyThrow< E >( (std::int32_t)t, u1, &tmp ); + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint64> +{ +public: + // T is std::int64_t + // U is std::uint64_t + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isUint64, "T must be Int64, U Uint64" ); + std::int64_t t1 = t; + std::uint64_t u1 = u; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::uint64_t >::RegMultiply( t1, u1, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const std::int64_t& t, const std::uint64_t& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isUint64, "T must be Int64, U Uint64"); + std::int64_t t1 = t; + std::uint64_t u1 = u; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::uint64_t >::template RegMultiplyThrow< E >( t1, u1, &tmp ); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntInt64> +{ +public: + // T is signed, up to 32-bit + // U is std::int64_t + _CONSTEXPR14 static bool Multiply( T t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isInt64, "U must be Int64" ); + std::int64_t u1 = u; + std::int32_t tmp = 0; + + if( LargeIntRegMultiply< std::int32_t, std::int64_t >::RegMultiply( (std::int32_t)t, u1, &tmp ) && + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "U must be Int64"); + std::int64_t u1 = u; + std::int32_t tmp = 0; + + LargeIntRegMultiply< std::int32_t, std::int64_t >::template RegMultiplyThrow< E >( (std::int32_t)t, u1, &tmp ); + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +enum DivisionState +{ + DivisionState_OK, + DivisionState_UnsignedSigned, + DivisionState_SignedUnsigned32, + DivisionState_SignedUnsigned64, + DivisionState_SignedUnsigned, + DivisionState_SignedSigned +}; + +template < typename T, typename U > class DivisionMethod +{ +public: + enum + { + method = (safeint_internal::type_compare< T, U >::isBothUnsigned ? DivisionState_OK : + (!std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed) ? DivisionState_UnsignedSigned : + (std::numeric_limits< T >::is_signed && + safeint_internal::int_traits< U >::isUint32 && + safeint_internal::int_traits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 : + (std::numeric_limits< T >::is_signed && safeint_internal::int_traits< U >::isUint64) ? DivisionState_SignedUnsigned64 : + (std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed) ? DivisionState_SignedUnsigned : + DivisionState_SignedSigned) + }; +}; + +template < typename T, typename U, int state > class DivisionHelper; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_OK > +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_UnsignedSigned> +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return SafeIntNoError; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return SafeIntNoError; + } + + return SafeIntArithmeticOverflow; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned32 > +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (std::int64_t)t/(std::int64_t)u ); + + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (std::int64_t)t/(std::int64_t)u ); + } +}; + +template < typename T, typename U, bool > class div_signed_uint64; +template < typename T, typename U> class div_signed_uint64 // Value of u fits into an int32 +{ +public: + _CONSTEXPR14 static T divide(T t, U u) { return (T)((std::int32_t)t / (std::int32_t)u); } +}; + +template < typename T, typename U> class div_signed_uint64 +{ +public: + _CONSTEXPR14 static T divide(T t, U u) { return (T)((std::int64_t)t / (std::int64_t)u); } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned64 > +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const std::uint64_t& u, T& result ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u <= (std::uint64_t)std::numeric_limits::max() ) + { + result = div_signed_uint64 < T, U, sizeof(T) < sizeof(std::int64_t) > ::divide(t, u); + } + else // Corner case + if( t == std::numeric_limits::min() && u == (std::uint64_t)std::numeric_limits::min() ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const std::uint64_t& u, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + if( u <= (std::uint64_t)std::numeric_limits::max() ) + { + result = div_signed_uint64 < T, U, sizeof(T) < sizeof(std::int64_t) > ::divide(t, u); + } + else // Corner case + if( t == std::numeric_limits::min() && u == (std::uint64_t)std::numeric_limits::min() ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned> +{ +public: + // T is any signed, U is unsigned and smaller than 32-bit + // In this case, standard operator casting is correct + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedSigned> +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Must test for corner case + if( t == std::numeric_limits::min() && u == (U)-1 ) + return SafeIntArithmeticOverflow; + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Must test for corner case + if( t == std::numeric_limits::min() && u == (U)-1 ) + E::SafeIntOnOverflow(); + + result = (T)( t/u ); + } +}; + +enum AdditionState +{ + AdditionState_CastIntCheckMax, + AdditionState_CastUintCheckOverflow, + AdditionState_CastUintCheckOverflowMax, + AdditionState_CastUint64CheckOverflow, + AdditionState_CastUint64CheckOverflowMax, + AdditionState_CastIntCheckSafeIntMinMax, + AdditionState_CastInt64CheckSafeIntMinMax, + AdditionState_CastInt64CheckMax, + AdditionState_CastUint64CheckSafeIntMinMax, + AdditionState_CastUint64CheckSafeIntMinMax2, + AdditionState_CastInt64CheckOverflow, + AdditionState_CastInt64CheckOverflowSafeIntMinMax, + AdditionState_CastInt64CheckOverflowMax, + AdditionState_ManualCheckInt64Uint64, + AdditionState_ManualCheck, + AdditionState_Error +}; + +template< typename T, typename U > +class AdditionMethod +{ +public: + enum + { + //unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow : + (IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax : + //unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax2 : + //signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowSafeIntMinMax : + //signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck : + AdditionState_Error) + }; +}; + +template < typename T, typename U, int method > class AdditionHelper; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //16-bit or less unsigned addition + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //16-bit or less unsigned addition + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflow > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflowMax> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflow> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflowMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 16-bit or less - one or both are signed + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() && tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 16-bit or less - one or both are signed + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() && tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - one or both are signed + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= (std::int64_t)std::numeric_limits::max() && tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - one or both are signed + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= (std::int64_t)std::numeric_limits::max() && tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - lhs signed, rhs unsigned + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - lhs signed, rhs unsigned + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is std::uint64_t, rhs signed + std::uint64_t tmp = 0; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return true; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is std::uint64_t, rhs signed + std::uint64_t tmp = 0; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax2> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is unsigned and < 64-bit, rhs std::int64_t + if( rhs < 0 ) + { + if( lhs >= ~(std::uint64_t)( rhs ) + 1 )//negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return true; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is unsigned and < 64-bit, rhs std::int64_t + if( rhs < 0 ) + { + if( lhs >= ~(std::uint64_t)( rhs ) + 1) //negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflow> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is std::int64_t, rhs signed + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs + (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + return false; + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is std::int64_t, rhs signed + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs + (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + E::SafeIntOnOverflow(); + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //rhs is std::int64_t, lhs signed + std::int64_t tmp = 0; + + if( AdditionHelper< std::int64_t, std::int64_t, AdditionState_CastInt64CheckOverflow >::Addition( (std::int64_t)lhs, (std::int64_t)rhs, tmp ) && + tmp <= std::numeric_limits::max() && + tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //rhs is std::int64_t, lhs signed + std::int64_t tmp = 0; + + AdditionHelper< std::int64_t, std::int64_t, AdditionState_CastInt64CheckOverflow >::AdditionThrow< E >( (std::int64_t)lhs, (std::int64_t)rhs, tmp ); + + if( tmp <= std::numeric_limits::max() && + tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowMax> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs is std::int64_t, rhs unsigned < 64-bit + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (T)(std::int64_t)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is std::int64_t, rhs unsigned < 64-bit + // Some compilers get optimization-happy, let's thwart them + + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (T)(std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheckInt64Uint64 > +{ +public: + _CONSTEXPR14 static bool Addition( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64" ); + // rhs is std::uint64_t, lhs std::int64_t + // cast everything to unsigned, perform addition, then + // cast back for check - this is done to stop optimizers from removing the code + std::uint64_t tmp = (std::uint64_t)lhs + rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (std::int64_t)tmp; + return true; + } + + result = 0; + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64"); + // rhs is std::uint64_t, lhs std::int64_t + std::uint64_t tmp = (std::uint64_t)lhs + rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheck> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // rhs is std::uint64_t, lhs signed, 32-bit or less + if( (std::uint32_t)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + // Note - this is tweaked to keep optimizers from tossing out the code. + std::uint32_t tmp = (std::uint32_t)rhs + (std::uint32_t)lhs; + + if( (std::int32_t)tmp >= lhs && SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( (std::int32_t)tmp, result ) ) + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // rhs is std::uint64_t, lhs signed, 32-bit or less + + if( (std::uint32_t)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + std::uint32_t tmp = (std::uint32_t)rhs + (std::uint32_t)lhs; + + if( (std::int32_t)tmp >= lhs ) + { + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( (std::int32_t)tmp, result ); + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +enum SubtractionState +{ + SubtractionState_BothUnsigned, + SubtractionState_CastIntCheckSafeIntMinMax, + SubtractionState_CastIntCheckMin, + SubtractionState_CastInt64CheckSafeIntMinMax, + SubtractionState_CastInt64CheckMin, + SubtractionState_Uint64Int, + SubtractionState_UintInt64, + SubtractionState_Int64Int, + SubtractionState_IntInt64, + SubtractionState_Int64Uint, + SubtractionState_IntUint64, + SubtractionState_Int64Uint64, + // states for SubtractionMethod2 + SubtractionState_BothUnsigned2, + SubtractionState_CastIntCheckSafeIntMinMax2, + SubtractionState_CastInt64CheckSafeIntMinMax2, + SubtractionState_Uint64Int2, + SubtractionState_UintInt642, + SubtractionState_Int64Int2, + SubtractionState_IntInt642, + SubtractionState_Int64Uint2, + SubtractionState_IntUint642, + SubtractionState_Int64Uint642, + SubtractionState_Error +}; + +template < typename T, typename U > class SubtractionMethod +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 : + SubtractionState_Error) + }; +}; + +// this is for the case of U - SafeInt< T, E > +template < typename T, typename U > class SubtractionMethod2 +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 : + SubtractionState_Error) + }; +}; + +template < typename T, typename U, int method > class SubtractionHelper; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, U& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + // Except we do have to check for overflow - lhs could be larger than result can hold + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + return SafeCastHelper< U, T, GetCastMethod::method>::Cast( tmp, result); + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, U& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + SafeCastHelper< U, T, GetCastMethod::method >::template CastThrow( tmp, result); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + if( SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, result ) ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastIntCheckSafeIntMinMax2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + return SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, result ); + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckMin > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + std::int32_t tmp = lhs - rhs; + + if( tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + std::int32_t tmp = lhs - rhs; + + if( tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + return SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::Cast( tmp, result ); + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastInt64CheckSafeIntMinMax2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + return SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::Cast( tmp, result ); + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckMin > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + if( tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + if( tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Uint64Int > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an std::uint64_t, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (std::uint64_t)rhs ); + return true; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an std::uint64_t, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (std::uint64_t)rhs ); + return; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Uint64Int2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U is std::uint64_t, T is signed + if( rhs < 0 ) + { + // treat this as addition + std::uint64_t tmp = 0; + + tmp = lhs + (std::uint64_t)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + else if( (std::uint64_t)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return true; + } + else + { + // result is positive + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U is std::uint64_t, T is signed + if( rhs < 0 ) + { + // treat this as addition + std::uint64_t tmp = 0; + + tmp = lhs + (std::uint64_t)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + else if( (std::uint64_t)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return; + } + else + { + // result is positive + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_UintInt64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an unsigned int32 or smaller, rhs std::int64_t + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return true; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + std::uint64_t tmp = lhs + ~(std::uint64_t)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= std::numeric_limits::max()) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an unsigned int32 or smaller, rhs std::int64_t + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + std::uint64_t tmp = lhs + ~(std::uint64_t)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= std::numeric_limits::max()) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_UintInt642 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U unsigned 32-bit or less, T std::int64_t + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (std::int64_t)lhs - rhs ); + return true; + } + else + { + // we effectively have an addition + // which cannot overflow internally + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)( -rhs ); + + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U unsigned 32-bit or less, T std::int64_t + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (std::int64_t)lhs - rhs ); + return; + } + else + { + // we effectively have an addition + // which cannot overflow internally + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)( -rhs ); + + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Int > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an std::int64_t, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an std::int64_t, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename T, typename U, bool > class subtract_corner_case_max; + +template < typename T, typename U> class subtract_corner_case_max < T, U, true> +{ +public: + _CONSTEXPR14 static bool isOverflowPositive(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (tmp > std::numeric_limits::max() || (rhs < 0 && tmp < lhs)); + } + + _CONSTEXPR14 static bool isOverflowNegative(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (tmp < std::numeric_limits::min() || (rhs >= 0 && tmp > lhs)); + } +}; + +template < typename T, typename U> class subtract_corner_case_max < T, U, false> +{ +public: + _CONSTEXPR14 static bool isOverflowPositive(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (rhs < 0 && tmp < lhs); + } + + _CONSTEXPR14 static bool isOverflowNegative(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (rhs >= 0 && tmp > lhs); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Int2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs std::int64_t, rhs any signed int (including std::int64_t) + std::int64_t tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if(subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit >::isOverflowPositive(rhs, lhs, tmp)) + { + return false; + } + } + else + { + // lhs negative + if(subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit >::isOverflowNegative(rhs, lhs, tmp)) + { + return false; + } + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs std::int64_t, rhs any signed int (including std::int64_t) + std::int64_t tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if (subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit>::isOverflowPositive(rhs, lhs, tmp)) + { + E::SafeIntOnOverflow(); + } + } + else + { + // lhs negative + if (subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit >::isOverflowNegative(rhs, lhs, tmp)) + { + E::SafeIntOnOverflow(); + } + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntInt64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 32-bit int or less, rhs std::int64_t + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + } + else + { + // fourth case + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 32-bit int or less, rhs std::int64_t + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + } + else + { + // fourth case + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntInt642 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int32 or smaller, rhs is int64 + std::int64_t tmp = (std::int64_t)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + return false; + //else OK + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int32 or smaller, rhs is int64 + std::int64_t tmp = (std::int64_t)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + //else OK + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (T)(std::int64_t)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint2 > +{ +public: + // lhs is std::int64_t, rhs is unsigned 32-bit or smaller + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // Do this as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= std::numeric_limits::max() && (std::int64_t)tmp >= std::numeric_limits::min() ) + { + result = (T)(std::int64_t)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // Do this as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= std::numeric_limits::max() && (std::int64_t)tmp >= std::numeric_limits::min() ) + { + result = (T)(std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntUint64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of std::numeric_limits::min() + // This will give it to us without extraneous compiler warnings + const std::uint64_t AbsMinIntT = (std::uint64_t)std::numeric_limits::max() + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return true; + } + } + else + { + if( rhs <= AbsMinIntT + (std::uint64_t)lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of std::numeric_limits::min() + // This will give it to us without extraneous compiler warnings + const std::uint64_t AbsMinIntT = (std::uint64_t)std::numeric_limits::max() + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return; + } + } + else + { + if( rhs <= AbsMinIntT + (std::uint64_t)lhs ) + { + result = (T)( lhs - rhs ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntUint642 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const std::int64_t& lhs, const std::uint64_t& rhs, std::int64_t& result ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64"); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (std::int64_t)tmp; + return true; + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64"); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint642 > +{ +public: + // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it + // get smaller. If rhs > lhs, then it would also go negative, which is the other case + _CONSTEXPR14 static bool Subtract( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64, "T must be Uint64, U Int64" ); + if( lhs >= 0 && (std::uint64_t)lhs >= rhs ) + { + result = (std::uint64_t)lhs - rhs; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64, "T must be Uint64, U Int64"); + if( lhs >= 0 && (std::uint64_t)lhs >= rhs ) + { + result = (std::uint64_t)lhs - rhs; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +enum BinaryState +{ + BinaryState_OK, + BinaryState_Int8, + BinaryState_Int16, + BinaryState_Int32 +}; + +template < typename T, typename U > class BinaryMethod +{ +public: + enum + { + // If both operands are unsigned OR + // return type is smaller than rhs OR + // return type is larger and rhs is unsigned + // Then binary operations won't produce unexpected results + method = ( sizeof( T ) <= sizeof( U ) || + safeint_internal::type_compare< T, U >::isBothUnsigned || + !std::numeric_limits< U >::is_signed ) ? BinaryState_OK : + safeint_internal::int_traits< U >::isInt8 ? BinaryState_Int8 : + safeint_internal::int_traits< U >::isInt16 ? BinaryState_Int16 + : BinaryState_Int32 + }; +}; + +#ifdef SAFEINT_DISABLE_BINARY_ASSERT +#define BinaryAssert(x) +#else +#define BinaryAssert(x) SAFEINT_ASSERT(x) +#endif + +template < typename T, typename U, int method > class BinaryAndHelper; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > +{ +public: + _CONSTEXPR11 static T And( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs & rhs ); } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > +{ +public: + _CONSTEXPR14 static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (std::uint8_t)rhs ) ); + return (T)( lhs & (std::uint8_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > +{ +public: + _CONSTEXPR14 static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (std::uint16_t)rhs ) ); + return (T)( lhs & (std::uint16_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 > +{ +public: + _CONSTEXPR14 static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (std::uint32_t)rhs ) ); + return (T)( lhs & (std::uint32_t)rhs ); + } +}; + +template < typename T, typename U, int method > class BinaryOrHelper; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > +{ +public: + _CONSTEXPR11 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs | rhs ); } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > +{ +public: + _CONSTEXPR14 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (std::uint8_t)rhs ) ); + return (T)( lhs | (std::uint8_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 > +{ +public: + _CONSTEXPR14 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (std::uint16_t)rhs ) ); + return (T)( lhs | (std::uint16_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 > +{ +public: + _CONSTEXPR14 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (std::uint32_t)rhs ) ); + return (T)( lhs | (std::uint32_t)rhs ); + } +}; + +template class BinaryXorHelper; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > +{ +public: + _CONSTEXPR11 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs ^ rhs ); } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > +{ +public: + _CONSTEXPR14 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (std::uint8_t)rhs ) ); + return (T)( lhs ^ (std::uint8_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > +{ +public: + _CONSTEXPR14 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (std::uint16_t)rhs ) ); + return (T)( lhs ^ (std::uint16_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > +{ +public: + _CONSTEXPR14 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (std::uint32_t)rhs ) ); + return (T)( lhs ^ (std::uint32_t)rhs ); + } +}; + +/***************** External functions ****************************************/ + +// External functions that can be used where you only need to check one operation +// non-class helper function so that you can check for a cast's validity +// and handle errors how you like +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeCast( const T From, U& To ) SAFEINT_NOTHROW +{ + return SafeCastHelper< U, T, GetCastMethod< U, T >::method >::Cast( From, To ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeNotEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeGreaterThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeGreaterThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeLessThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeLessThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeModulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW +{ + return ( ModulusHelper< T, U, ValidComparison< T, U >::method >::Modulus( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +_CONSTEXPR14_MULTIPLY inline bool SafeMultiply( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::Multiply( t, u, result ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeDivide( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return ( DivisionHelper< T, U, DivisionMethod< T, U >::method >::Divide( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeAdd( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return AdditionHelper< T, U, AdditionMethod< T, U >::method >::Addition( t, u, result ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeSubtract( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::Subtract( t, u, result ); +} + +/***************** end external functions ************************************/ + +// Main SafeInt class +// Assumes exceptions can be thrown +template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeInt +{ +public: + _CONSTEXPR11 SafeInt() SAFEINT_NOTHROW : m_int(0) + { + static_assert( safeint_internal::numeric_type< T >::isInt, "Integer type required" ); + } + + // Having a constructor for every type of int + // avoids having the compiler evade our checks when doing implicit casts - + // e.g., SafeInt s = 0x7fffffff; + _CONSTEXPR11 SafeInt( const T& i ) SAFEINT_NOTHROW : m_int(i) + { + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + //always safe + } + + // provide explicit boolean converter + _CONSTEXPR11 SafeInt( bool b ) SAFEINT_NOTHROW : m_int((T)(b ? 1 : 0)) + { + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + } + + template < typename U > + _CONSTEXPR14 SafeInt(const SafeInt< U, E >& u) SAFEINT_CPP_THROW : m_int(0) + { + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + m_int = (T)SafeInt< T, E >( (U)u ); + } + + template < typename U > + _CONSTEXPR14 SafeInt( const U& i ) SAFEINT_CPP_THROW : m_int(0) + { + // m_int must be initialized to something to work with constexpr, because if it throws, then m_int is unknown + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + // SafeCast will throw exceptions if i won't fit in type T + + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( i, m_int ); + } + + // The destructor is intentionally commented out - no destructor + // vs. a do-nothing destructor makes a huge difference in + // inlining characteristics. It wasn't doing anything anyway. + // ~SafeInt(){}; + + + // now start overloading operators + // assignment operator + // constructors exist for all int types and will ensure safety + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator =( const U& rhs ) SAFEINT_CPP_THROW + { + // use constructor to test size + // constructor is optimized to do minimal checking based + // on whether T can contain U + // note - do not change this + m_int = SafeInt< T, E >( rhs ); + return *this; + } + + _CONSTEXPR14 SafeInt< T, E >& operator =( const T& rhs ) SAFEINT_NOTHROW + { + m_int = rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator =( const SafeInt< U, E >& rhs ) SAFEINT_CPP_THROW + { + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( rhs.Ref(), m_int ); + return *this; + } + + _CONSTEXPR14 SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) SAFEINT_NOTHROW + { + m_int = rhs.m_int; + return *this; + } + + // Casting operators + + _CONSTEXPR11 operator bool() const SAFEINT_NOTHROW + { + return !!m_int; + } + + _CONSTEXPR14 operator char() const SAFEINT_CPP_THROW + { + char val = 0; + SafeCastHelper< char, T, GetCastMethod< char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator signed char() const SAFEINT_CPP_THROW + { + signed char val = 0; + SafeCastHelper< signed char, T, GetCastMethod< signed char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned char() const SAFEINT_CPP_THROW + { + unsigned char val = 0; + SafeCastHelper< unsigned char, T, GetCastMethod< unsigned char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator short() const SAFEINT_CPP_THROW + { + short val = 0; + SafeCastHelper< short, T, GetCastMethod< short, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned short() const SAFEINT_CPP_THROW + { + unsigned short val = 0; + SafeCastHelper< unsigned short, T, GetCastMethod< unsigned short, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator int() const SAFEINT_CPP_THROW + { + int val = 0; + SafeCastHelper< int, T, GetCastMethod< int, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned int() const SAFEINT_CPP_THROW + { + unsigned int val = 0; + SafeCastHelper< unsigned int, T, GetCastMethod< unsigned int, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // The compiler knows that int == std::int32_t + // but not that long == std::int32_t, because on some systems, long == std::int64_t + _CONSTEXPR14 operator long() const SAFEINT_CPP_THROW + { + long val = 0; + SafeCastHelper< long, T, GetCastMethod< long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned long() const SAFEINT_CPP_THROW + { + unsigned long val = 0; + SafeCastHelper< unsigned long, T, GetCastMethod< unsigned long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator long long() const SAFEINT_CPP_THROW + { + long long val = 0; + SafeCastHelper< long long, T, GetCastMethod< long long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned long long() const SAFEINT_CPP_THROW + { + unsigned long long val = 0; + SafeCastHelper< unsigned long long, T, GetCastMethod< unsigned long long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator wchar_t() const SAFEINT_CPP_THROW + { + wchar_t val = 0; + SafeCastHelper< wchar_t, T, GetCastMethod< wchar_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + +#ifdef SIZE_T_CAST_NEEDED + // We also need an explicit cast to size_t, or the compiler will complain + // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them + // Leave here in case we decide to backport this to an earlier compiler + _CONSTEXPR14 operator size_t() const SAFEINT_CPP_THROW + { + size_t val = 0; + SafeCastHelper< size_t, T, GetCastMethod< size_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } +#endif + + // Also provide a cast operator for floating point types + _CONSTEXPR14 operator float() const SAFEINT_CPP_THROW + { + float val = 0.0; + SafeCastHelper< float, T, GetCastMethod< float, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator double() const SAFEINT_CPP_THROW + { + double val = 0.0; + SafeCastHelper< double, T, GetCastMethod< double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + _CONSTEXPR14 operator long double() const SAFEINT_CPP_THROW + { + long double val = 0.0; + SafeCastHelper< long double, T, GetCastMethod< long double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // If you need a pointer to the data + // this could be dangerous, but allows you to correctly pass + // instances of this class to APIs that take a pointer to an integer + // also see overloaded address-of operator below + T* Ptr() SAFEINT_NOTHROW { return &m_int; } + const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } + _CONSTEXPR14 const T& Ref() const SAFEINT_NOTHROW { return m_int; } + +#if 0 + // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload + // operator & + // This allows you to do unsafe things! + // It is meant to allow you to more easily + // pass a SafeInt into things like ReadFile + T* operator &() SAFEINT_NOTHROW { return &m_int; } + const T* operator &() const SAFEINT_NOTHROW { return &m_int; } +#endif + + // Unary operators + _CONSTEXPR11 bool operator !() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } + + // operator + (unary) + // note - normally, the '+' and '-' operators will upcast to a signed int + // for T < 32 bits. This class changes behavior to preserve type + _CONSTEXPR11 const SafeInt< T, E >& operator +() const SAFEINT_NOTHROW { return *this; } + + //unary - + + _CONSTEXPR14 SafeInt< T, E > operator -() const SAFEINT_CPP_THROW + { + // Note - unsigned still performs the bitwise manipulation + // will warn at level 2 or higher if the value is 32-bit or larger + return SafeInt(NegationHelper::is_signed>::template NegativeThrow(m_int)); + } + + // prefix increment operator + _CONSTEXPR14 SafeInt< T, E >& operator ++() SAFEINT_CPP_THROW + { + if( m_int != std::numeric_limits::max() ) + { + ++m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // prefix decrement operator + _CONSTEXPR14 SafeInt< T, E >& operator --() SAFEINT_CPP_THROW + { + if( m_int != std::numeric_limits::min() ) + { + --m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // note that postfix operators have inherently worse perf + // characteristics + + // postfix increment operator + _CONSTEXPR14 SafeInt< T, E > operator ++( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != std::numeric_limits::max() ) + { + SafeInt< T, E > tmp( m_int ); + + m_int++; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // postfix decrement operator + _CONSTEXPR14 SafeInt< T, E > operator --( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != std::numeric_limits::min() ) + { + SafeInt< T, E > tmp( m_int ); + m_int--; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // One's complement + // Note - this operator will normally change size to an int + // cast in return improves perf and maintains type + _CONSTEXPR11 SafeInt< T, E > operator ~() const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)~m_int ); } + + // Binary operators + // + // arithmetic binary operators + // % modulus + // * multiplication + // / division + // + addition + // - subtraction + // + // For each of the arithmetic operators, you will need to + // use them as follows: + // + // SafeInt c = 2; + // SafeInt i = 3; + // + // SafeInt i2 = i op (char)c; + // OR + // SafeInt i2 = (int)i op c; + // + // The base problem is that if the lhs and rhs inputs are different SafeInt types + // it is not possible in this implementation to determine what type of SafeInt + // should be returned. You have to let the class know which of the two inputs + // need to be the return type by forcing the other value to the base integer type. + // + // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. + // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, + // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer + // is situational. + // + // The case of: + // + // SafeInt< T, E > i, j, k; + // i = j op k; + // + // works just fine and no unboxing is needed because the return type is not ambiguous. + + // Modulus + // Modulus has some convenient properties - + // first, the magnitude of the return can never be + // larger than the lhs operand, and it must be the same sign + // as well. It does, however, suffer from the same promotion + // problems as comparisons, division and other operations + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator %( U rhs ) const SAFEINT_CPP_THROW + { + T result = 0; + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + _CONSTEXPR14 SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T result = 0; + ModulusHelper< T, T, ValidComparison< T, T >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + // Modulus assignment + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator %=( U rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Multiplication + template < typename U > + _CONSTEXPR14_MULTIPLY SafeInt< T, E > operator *( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + _CONSTEXPR14 SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Multiplication assignment + _CONSTEXPR14 SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14_MULTIPLY SafeInt< T, E >& operator *=( U rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14_MULTIPLY SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs.Ref(), m_int ); + return *this; + } + + // Division + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator /( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + _CONSTEXPR14 SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Division assignment + _CONSTEXPR14 SafeInt< T, E >& operator /=( SafeInt< T, E > i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)i, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator /=( U i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, i, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator /=( SafeInt< U, E > i ) + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, (U)i, m_int ); + return *this; + } + + // For addition and subtraction + + // Addition + _CONSTEXPR14 SafeInt< T, E > operator +( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator +( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + //addition assignment + _CONSTEXPR14 SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator +=( U rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Subtraction + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator -( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + _CONSTEXPR14 SafeInt< T, E > operator -(SafeInt< T, E > rhs) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Subtraction assignment + _CONSTEXPR14 SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator -=( U rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Shift operators + // Note - shift operators ALWAYS return the same type as the lhs + // specific version for SafeInt< T, E > not needed - + // code path is exactly the same as for SafeInt< U, E > as rhs + + // Left shift + // Also, shifting > bitcount is undefined - trap in debug + #define ShiftAssert(x) SAFEINT_ASSERT(x) + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << bits ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + } + + // Left shift assignment + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int <<= bits; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int <<= (U)bits; + return *this; + } + + // Right shift + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int >> bits ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + } + + // Right shift assignment + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int >>= bits; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int >>= (U)bits; + return *this; + } + + // Bitwise operators + // This only makes sense if we're dealing with the same type and size + // demand a type T, or something that fits into a type T + + // Bitwise & + _CONSTEXPR14 SafeInt< T, E > operator &( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( m_int & (T)rhs ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator &( U rhs ) const SAFEINT_NOTHROW + { + // we want to avoid setting bits by surprise + // consider the case of lhs = int, value = 0xffffffff + // rhs = char, value = 0xff + // + // programmer intent is to get only the lower 8 bits + // normal behavior is to upcast both sides to an int + // which then sign extends rhs, setting all the bits + + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ) ); + } + + // Bitwise & assignment + _CONSTEXPR14 SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int &= (T)rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator &=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, (U)rhs ); + return *this; + } + + // XOR + _CONSTEXPR14 SafeInt< T, E > operator ^( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int ^ (T)rhs ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator ^( U rhs ) const SAFEINT_NOTHROW + { + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ) ); + } + + // XOR assignment + _CONSTEXPR14 SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int ^= (T)rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator ^=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, (U)rhs ); + return *this; + } + + // bitwise OR + _CONSTEXPR14 SafeInt< T, E > operator |( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int | (T)rhs ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator |( U rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ) ); + } + + // bitwise OR assignment + _CONSTEXPR14 SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int |= (T)rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator |=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, (U)rhs ); + return *this; + } + + // Miscellaneous helper functions + SafeInt< T, E > Min( SafeInt< T, E > test, const T floor = std::numeric_limits::min() ) const SAFEINT_NOTHROW + { + T tmp = test < m_int ? (T)test : m_int; + return tmp < floor ? floor : tmp; + } + + SafeInt< T, E > Max( SafeInt< T, E > test, const T upper = std::numeric_limits::max() ) const SAFEINT_NOTHROW + { + T tmp = test > m_int ? (T)test : m_int; + return tmp > upper ? upper : tmp; + } + + void Swap( SafeInt< T, E >& with ) SAFEINT_NOTHROW + { + T temp( m_int ); + m_int = with.m_int; + with.m_int = temp; + } + + static SafeInt< T, E > SafeAtoI( const char* input ) SAFEINT_CPP_THROW + { + return SafeTtoI( input ); + } + + static SafeInt< T, E > SafeWtoI( const wchar_t* input ) + { + return SafeTtoI( input ); + } + + enum alignBits + { + align2 = 1, + align4 = 2, + align8 = 3, + align16 = 4, + align32 = 5, + align64 = 6, + align128 = 7, + align256 = 8 + }; + + template < alignBits bits > + const SafeInt< T, E >& Align() SAFEINT_CPP_THROW + { + // Zero is always aligned + if( m_int == 0 ) + return *this; + + // We don't support aligning negative numbers at this time + // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) + // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). + // Also makes no sense to try to align on negative or no bits. + + ShiftAssert( ( ( std::numeric_limits< T >::is_signed && bits < (int)safeint_internal::int_traits< T >::bitCount - 1 ) + || ( !std::numeric_limits< T >::is_signed && bits < (int)safeint_internal::int_traits< T >::bitCount ) ) && + bits >= 0 && ( !std::numeric_limits< T >::is_signed || m_int > 0 ) ); + + const T AlignValue = ( (T)1 << bits ) - 1; + + m_int = (T)( ( m_int + AlignValue ) & ~AlignValue ); + + if( m_int <= 0 ) + E::SafeIntOnOverflow(); + + return *this; + } + + // Commonly needed alignments: + const SafeInt< T, E >& Align2() { return Align< align2 >(); } + const SafeInt< T, E >& Align4() { return Align< align4 >(); } + const SafeInt< T, E >& Align8() { return Align< align8 >(); } + const SafeInt< T, E >& Align16() { return Align< align16 >(); } + const SafeInt< T, E >& Align32() { return Align< align32 >(); } + const SafeInt< T, E >& Align64() { return Align< align64 >(); } +private: + + // This is almost certainly not the best optimized version of atoi, + // but it does not display a typical bug where it isn't possible to set MinInt + // and it won't allow you to overflow your integer. + // This is here because it is useful, and it is an example of what + // can be done easily with SafeInt. + template < typename U > + static SafeInt< T, E > SafeTtoI( U* input ) SAFEINT_CPP_THROW + { + U* tmp = input; + SafeInt< T, E > s; + bool negative = false; + + // Bad input, or empty string + if( input == nullptr || input[0] == 0 ) + E::SafeIntOnOverflow(); + + switch( *tmp ) + { + case '-': + tmp++; + negative = true; + break; + case '+': + tmp++; + break; + } + + while( *tmp != 0 ) + { + if( *tmp < '0' || *tmp > '9' ) + break; + + if( (T)s != 0 ) + s *= (T)10; + + if( !negative ) + s += (T)( *tmp - '0' ); + else + s -= (T)( *tmp - '0' ); + + tmp++; + } + + return s; + } + + T m_int; +}; + +// Helper function used to subtract pointers. +// Used to squelch warnings +template +_CONSTEXPR11 SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW +{ + return SafeInt( p1 - p2 ); +} + +// Comparison operators + +//Less than +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, (U)lhs ); +} + +// Greater than +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// Greater than or equal +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >=( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( (U)rhs, (T)lhs ); +} + +// Less than or equal +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// equality +// explicit overload for bool +template < typename T, typename E > +_CONSTEXPR11 bool operator ==( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return lhs == ( (T)rhs == 0 ? false : true ); +} + +template < typename T, typename E > +_CONSTEXPR11 bool operator ==( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return rhs == ( (T)lhs == 0 ? false : true ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator ==( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals((T)rhs, lhs); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator ==( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, (U)rhs ); +} + +//not equals +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator !=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator !=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( lhs, rhs ); +} + + +template < typename T, typename E > +_CONSTEXPR11 bool operator !=( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return ( (T)rhs == 0 ? false : true ) != lhs; +} + +template < typename T, typename E > +_CONSTEXPR11 bool operator !=( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return ( (T)lhs == 0 ? false : true ) != rhs; +} + + +template < typename T, typename U, typename E, int method > class ModulusSimpleCaseHelper; + +template < typename T, typename E, int method > class ModulusSignedCaseHelper; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > +{ +public: + _CONSTEXPR14 static bool SignedCase( SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_NOTHROW + { + if( (T)rhs == (T)-1 ) + { + result = 0; + return true; + } + return false; + } +}; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, false > +{ +public: + _CONSTEXPR11 static bool SignedCase( SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, true > +{ +public: + _CONSTEXPR14 static bool ModulusSimpleCase( U lhs, SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_CPP_THROW + { + if( rhs != 0 ) + { + if( ModulusSignedCaseHelper< T, E, std::numeric_limits< T >::is_signed >::SignedCase( rhs, result ) ) + return true; + + result = (T)( lhs % (T)rhs ); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template< typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, false > +{ +public: + _CONSTEXPR11 static bool ModulusSimpleCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Modulus +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Value of return depends on sign of lhs + // This one may not be safe - bounds check in constructor + // if lhs is negative and rhs is unsigned, this will throw an exception. + + // Fast-track the simple case + // same size and same sign + SafeInt< T, E > result; + + if( ModulusSimpleCaseHelper< T, U, E, (sizeof(T) == sizeof(U)) && ((bool)std::numeric_limits< T >::is_signed == (bool)std::numeric_limits< U >::is_signed) >::ModulusSimpleCase( lhs, rhs, result ) ) + return result; + + result = (SafeInt< U, E >(lhs) % (T)rhs); + return result; +} + +// Multiplication +template < typename T, typename U, typename E > +_CONSTEXPR14_MULTIPLY SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >(ret); +} + +template < typename T, typename U, typename E, int method > class DivisionNegativeCornerCaseHelper; + +template < typename T, typename U, bool > class division_negative_negateU; + +template < typename T, typename U > class division_negative_negateU< T, U, true> +{ +public: + // sizeof(T) == 4 + _CONSTEXPR14 static U div(T rhs, U lhs) { return lhs / (U)(~(std::uint32_t)(T)rhs + 1); } +}; + +template < typename T, typename U > class division_negative_negateU< T, U, false> +{ +public: + _CONSTEXPR14 static U div(T rhs, U lhs) { return lhs / (U)(~(std::uint64_t)(T)rhs + 1); } +}; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, true > +{ +public: + static bool NegativeCornerCase( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + // Problem case - normal casting behavior changes meaning + // flip rhs to positive + // any operator casts now do the right thing + U tmp = division_negative_negateU< T, U, sizeof(T) == 4>::div(rhs, lhs); + + if( tmp <= (U)std::numeric_limits::max() ) + { + result = SafeInt< T, E >( (T)(~(std::uint64_t)tmp + 1) ); + return true; + } + + // Corner case + T maxT = std::numeric_limits::max(); + if( tmp == (U)maxT + 1 ) + { + T minT = std::numeric_limits::min(); + result = SafeInt< T, E >( minT ); + return true; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, false > +{ +public: + _CONSTEXPR11 static bool NegativeCornerCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, true > +{ +public: + _CONSTEXPR14 static bool DivisionCornerCase1( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( (T)rhs > 0 ) + { + result = SafeInt< T, E >( lhs/(T)rhs ); + return true; + } + + // Now rhs is either negative, or zero + if( (T)rhs != 0 ) + { + if( DivisionNegativeCornerCaseHelper< T, U, E, sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) >::NegativeCornerCase( lhs, rhs, result ) ) + return true; + + result = SafeInt< T, E >(lhs/(T)rhs); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, false > +{ +public: + _CONSTEXPR11 static bool DivisionCornerCase1( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper2; + +template < typename T, typename U, bool > class div_negate_min; + +template < typename T, typename U > class div_negate_min < T, U , true > +{ +public: + _CONSTEXPR14 static bool Value(T& ret) + { + ret = (T)(-(T)std::numeric_limits< U >::min()); + return true; + } +}; + +template < typename T, typename U > class div_negate_min < T, U, false > +{ +public: + _CONSTEXPR14 static bool Value(T& ) + { + return false; + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, true > +{ +public: + _CONSTEXPR14 static bool DivisionCornerCase2( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( lhs == std::numeric_limits< U >::min() && (T)rhs == -1 ) + { + // corner case of a corner case - lhs = min int, rhs = -1, + // but rhs is the return type, so in essence, we can return -lhs + // if rhs is a larger type than lhs + // If types are wrong, throws + T tmp = 0; + + if (div_negate_min< T, U, sizeof(U) < sizeof(T) > ::Value(tmp)) + result = tmp; + else + E::SafeIntOnOverflow(); + + return true; + } + + return false; + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, false > +{ +public: + _CONSTEXPR11 static bool DivisionCornerCase2( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Division +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator /( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Corner case - has to be handled seperately + SafeInt< T, E > result; + if( DivisionCornerCaseHelper< T, U, E, (int)DivisionMethod< U, T >::method == (int)DivisionState_UnsignedSigned >::DivisionCornerCase1( lhs, rhs, result ) ) + return result; + + if( DivisionCornerCaseHelper2< T, U, E, safeint_internal::type_compare< T, U >::isBothSigned >::DivisionCornerCase2( lhs, rhs, result ) ) + return result; + + // Otherwise normal logic works with addition of bounds check when casting from U->T + U ret = 0; + DivisionHelper< U, T, DivisionMethod< U, T >::method >::template DivideThrow< E >( lhs, (T)rhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Addition +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Subtraction +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< U, T, SubtractionMethod2< U, T >::method >::template SubtractThrow< E >( lhs, rhs.Ref(), ret ); + + return SafeInt< T, E >( ret ); +} + +// Overrides designed to deal with cases where a SafeInt is assigned out +// to a normal int - this at least makes the last operation safe +// += +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator +=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator -=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator *=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator /=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator %=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator &=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator ^=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator |=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator <<=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); + return lhs; +} + +// Specific pointer overrides +// Note - this function makes no attempt to ensure +// that the resulting pointer is still in the buffer, only +// that no int overflows happened on the way to getting the new pointer +template < typename T, typename U, typename E > +T*& operator +=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + // Note: this doesn't really make sense as a constexpr, but cannot be because of the reinterpret_cast + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // Check first that rhs is valid for the type of ptrdiff_t + // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t + // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff + // Finally, cast the number back to a pointer of the correct type + lhs = reinterpret_cast< T* >( (size_t)( ptr_val + (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // See above for comments + lhs = reinterpret_cast< T* >( (size_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator *=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert( sizeof(T) == 0, "Unsupported operator" ); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator /=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator %=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator &=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator ^=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator |=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T*& operator <<=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +// Shift operators +// NOTE - shift operators always return the type of the lhs argument + +// Left shift +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !std::numeric_limits< T >::is_signed || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)safeint_internal::int_traits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs << (T)bits ) ); +} + +// Right shift +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !std::numeric_limits< T >::is_signed || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)safeint_internal::int_traits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); +} + +// Bitwise operators +// This only makes sense if we're dealing with the same type and size +// demand a type T, or something that fits into a type T. + +// Bitwise & +template < typename T, typename U, typename E > +_CONSTEXPR11 SafeInt< T, E > operator &( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( (T)rhs, lhs ) ); +} + +// Bitwise XOR +template < typename T, typename U, typename E > +_CONSTEXPR11 SafeInt< T, E > operator ^( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >(BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( (T)rhs, lhs ) ); +} + +// Bitwise OR +template < typename T, typename U, typename E > +_CONSTEXPR11 SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( (T)rhs, lhs ) ); +} + +#endif //SAFEINT_HPP + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +} // utilities +} // msl +#endif + diff --git a/hilti/include/3rdparty/SafeInt/SafeInt.hpp.orig b/hilti/include/3rdparty/SafeInt/SafeInt.hpp.orig new file mode 100644 index 000000000..2c3797270 --- /dev/null +++ b/hilti/include/3rdparty/SafeInt/SafeInt.hpp.orig @@ -0,0 +1,7103 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/*----------------------------------------------------------------------------------------------------------- +SafeInt.hpp +Version 3.0.20p + +This header implements an integer handling class designed to catch +unsafe integer operations + +This header compiles properly at Wall on Visual Studio, -Wall on gcc, and -Weverything on clang. +Tested most recently on clang 3.8.0, gcc 7.3.1, and both Visual Studio 2015 and 2017. + +Please read the leading comments before using the class. +---------------------------------------------------------------*/ +#ifndef SAFEINT_HPP +#define SAFEINT_HPP + +// It is a bit tricky to sort out what compiler we are actually using, +// do this once here, and avoid cluttering the code +#define VISUAL_STUDIO_COMPILER 0 +#define CLANG_COMPILER 1 +#define GCC_COMPILER 2 +#define UNKNOWN_COMPILER -1 + +// Clang will sometimes pretend to be Visual Studio +// and does pretend to be gcc. Check it first, as nothing else pretends to be clang +#if defined __clang__ +#define SAFEINT_COMPILER CLANG_COMPILER +#elif defined __GNUC__ +#define SAFEINT_COMPILER GCC_COMPILER +#elif defined _MSC_VER +#define SAFEINT_COMPILER VISUAL_STUDIO_COMPILER +#else +#define SAFEINT_COMPILER UNKNOWN_COMPILER +#endif + +#define CPLUSPLUS_98 0 +#define CPLUSPLUS_11 1 +#define CPLUSPLUS_14 2 +#define CPLUSPLUS_17 3 // Future use + +// Determine C++ support level +#if SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER + +#if __cplusplus < 201103L +#define CPLUSPLUS_STD CPLUSPLUS_98 +#elif __cplusplus < 201402L +#define CPLUSPLUS_STD CPLUSPLUS_11 +#else +#define CPLUSPLUS_STD CPLUSPLUS_14 +#endif + +#elif SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER + +// This needs additional testing to get more versions of _MSCVER +#if _MSC_VER < 1900 // Prior to VS 2015, need more testing to determine support +#define CPLUSPLUS_STD CPLUSPLUS_98 + +#elif _MSC_VER < 1910 // VS 2015 +#define CPLUSPLUS_STD CPLUSPLUS_11 + +#else // VS 2017 or later +// Note - there is a __cpp_constexpr test now, but everything prior to VS 2017 reports incorrect values +// and this version always supports at least the CPLUSPLUS_14 approach +#define CPLUSPLUS_STD CPLUSPLUS_14 + +#endif + +#else +// Unknown compiler, assume C++ 98 +#define CPLUSPLUS_STD CPLUSPLUS_98 +#endif // Determine C++ support level + +#if (SAFEINT_COMPILER == CLANG_COMPILER || SAFEINT_COMPILER == GCC_COMPILER) && CPLUSPLUS_STD < CPLUSPLUS_11 +#error Must compile with --std=c++11, preferably --std=c++14 to use constexpr improvements +#endif + +#define CONSTEXPR_NONE 0 +#define CONSTEXPR_CPP11 1 +#define CONSTEXPR_CPP14 2 + +// Let's try to use the new standard to determine feature compliance +// If the user has an unknown compiler, or just for testing, allow forcing this setting +#if !defined CONSTEXPR_SUPPORT + +#if defined __cpp_constexpr +// If it is gcc or clang, at least recent versions, then we have -std=c++11 or -std=c++14 +// This won't be set otherwise, but the headers won't compile, either +#if __cpp_constexpr >= 201304L +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP14 // Clang, gcc, Visual Studio 2017 or later +#elif __cpp_constexpr >= 200704L +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP11 // Clang, gcc with -std=c++11, Visual Studio 2015 +#else +#define CONSTEXPR_SUPPORT CONSTEXPR_NONE +#endif + +#else // !defined __cpp_constexpr +// Visual Studio is somehow not playing nice. shows __cpp_constexpr visually as defined, but won't compile +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#if CPLUSPLUS_STD == CPLUSPLUS_14 +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP14 +#elif CPLUSPLUS_STD == CPLUSPLUS_11 +#define CONSTEXPR_SUPPORT CONSTEXPR_CPP11 +#else +#define CONSTEXPR_SUPPORT CONSTEXPR_NONE +#endif +#else +#define CONSTEXPR_SUPPORT CONSTEXPR_NONE +#endif + +#endif // defined __cpp_constexpr + +#endif // !defined CONSTEXPR_SUPPORT + + +#if CONSTEXPR_SUPPORT == CONSTEXPR_NONE +#define _CONSTEXPR11 +#define _CONSTEXPR14 +#elif CONSTEXPR_SUPPORT == CONSTEXPR_CPP11 +#define _CONSTEXPR11 constexpr +#define _CONSTEXPR14 +#elif CPLUSPLUS_STD >= CPLUSPLUS_14 +#define _CONSTEXPR11 constexpr +#define _CONSTEXPR14 constexpr +#else +#error "Unexpected value of CPLUSPLUS_STD" +#endif + +// Enable compiling with /Wall under VC +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +// Off by default - unreferenced inline function has been removed +// Note - this intentionally leaks from the header, doesn't quench the warnings otherwise +#pragma warning( disable: 4514 ) + +#pragma warning( push ) +// Disable warnings coming from headers +#pragma warning( disable:4987 4820 4987 4820 ) +#endif + +// More defines to accomodate compiler differences +#if SAFEINT_COMPILER == GCC_COMPILER || SAFEINT_COMPILER == CLANG_COMPILER +#define SAFEINT_NORETURN __attribute__((noreturn)) +#define SAFEINT_STDCALL +#define SAFEINT_VISIBLE __attribute__ ((__visibility__("default"))) +#define SAFEINT_WEAK __attribute__ ((weak)) +#else +#define SAFEINT_NORETURN __declspec(noreturn) +#define SAFEINT_STDCALL __stdcall +#define SAFEINT_VISIBLE +#define SAFEINT_WEAK +#endif + +// On the Microsoft compiler, violating a throw() annotation is a silent error. +// Other compilers might turn these into exceptions, and some users may want to not have throw() enabled. +// In addition, some error handlers may not throw C++ exceptions, which makes everything no throw. +#if defined SAFEINT_REMOVE_NOTHROW +#define SAFEINT_NOTHROW +#else +#define SAFEINT_NOTHROW noexcept +#endif + +#include +#include +#include // This is now required +// Need this for ptrdiff_t on some compilers +#include +#include // Needed for floating point implementation + +// Note - intrinsics and constexpr are mutually exclusive +// If it is important to get constexpr for multiplication, then define SAFEINT_USE_INTRINSICS 0 +// However, intrinsics will result in much smaller code, and should have better perf +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER && defined _M_AMD64 && !defined SAFEINT_USE_INTRINSICS + #include + #define SAFEINT_USE_INTRINSICS 1 + #define _CONSTEXPR14_MULTIPLY +#else + #define SAFEINT_USE_INTRINSICS 0 + #define _CONSTEXPR14_MULTIPLY _CONSTEXPR14 +#endif + +// If you would like to use your own custom assert +// Define SAFEINT_ASSERT +#if !defined SAFEINT_ASSERT +#include +#define SAFEINT_ASSERT(x) assert(x) +#endif + +#if SAFEINT_COMPILER == VISUAL_STUDIO_COMPILER +#pragma warning( pop ) +#endif + +#if !defined _CRT_SECURE_INVALID_PARAMETER +// Calling fail fast is somewhat more robust than calling abort, +// but abort is the closest we can manage without Visual Studio support +// Need the header for abort() +#include +#define _CRT_SECURE_INVALID_PARAMETER(msg) abort() +#endif + +// Let's test some assumptions +// We're assuming two's complement negative numbers +static_assert( -1 == static_cast(0xffffffff), "Two's complement signed numbers are required" ); + +/************* Compiler Options ***************************************************************************************************** + +SafeInt supports several compile-time options that can change the behavior of the class. + +Compiler options: +SAFEINT_ASSERT_ON_EXCEPTION - it is often easier to stop on an assert and figure out a problem than to try and figure out + how you landed in the catch block. +SafeIntDefaultExceptionHandler - if you'd like to replace the exception handlers SafeInt provides, define your replacement and + define this. Note - two built in (Windows-specific) options exist: + - SAFEINT_FAILFAST - bypasses all exception handlers, exits the app with an exception + - SAFEINT_RAISE_EXCEPTION - throws Win32 exceptions, which can be caught +SAFEINT_DISALLOW_UNSIGNED_NEGATION - Invoking the unary negation operator may create warnings, but if you'd like it to completely fail + to compile, define this. +SAFEINT_DISABLE_BINARY_ASSERT - binary AND, OR or XOR operations on mixed size types can produce unexpected results. If you do + this, the default is to assert. Set this if you prefer not to assert under these conditions. +SIZE_T_CAST_NEEDED - some compilers complain if there is not a cast to size_t, others complain if there is one. + This lets you not have your compiler complain. + +************************************************************************************************************************************/ + +/* +* The SafeInt class is designed to have as low an overhead as possible +* while still ensuring that all integer operations are conducted safely. +* Nearly every operator has been overloaded, with a very few exceptions. +* +* A usability-safety trade-off has been made to help ensure safety. This +* requires that every operation return either a SafeInt or a bool. If we +* allowed an operator to return a base integer type T, then the following +* can happen: +* +* char i = SafeInt(32) * 2 + SafeInt(16) * 4; +* +* The * operators take precedence, get overloaded, return a char, and then +* you have: +* +* char i = (char)64 + (char)64; //overflow! +* +* This situation would mean that safety would depend on usage, which isn't +* acceptable. +* +* One key operator that is missing is an implicit cast to type T. The reason for +* this is that if there is an implicit cast operator, then we end up with +* an ambiguous compile-time precedence. Because of this amiguity, there +* are two methods that are provided: +* +* Casting operators for every native integer type +* Version 3 note - it now compiles correctly for size_t without warnings +* +* SafeInt::Ptr() - returns the address of the internal integer +* Note - the '&' (address of) operator has been overloaded and returns +* the address of the internal integer. +* +* The SafeInt class should be used in any circumstances where ensuring +* integrity of the calculations is more important than performance. See Performance +* Notes below for additional information. +* +* Many of the conditionals will optimize out or be inlined for a release +* build (especially with /Ox), but it does have significantly more overhead, +* especially for signed numbers. If you do not _require_ negative numbers, use +* unsigned integer types - certain types of problems cannot occur, and this class +* performs most efficiently. +* +* Here's an example of when the class should ideally be used - +* +* void* AllocateMemForStructs(int StructSize, int HowMany) +* { +* SafeInt s(StructSize); +* +* s *= HowMany; +* +* return malloc(s); +* +* } +* +* Here's when it should NOT be used: +* +* void foo() +* { +* int i; +* +* for(i = 0; i < 0xffff; i++) +* .... +* } +* +* Error handling - a SafeInt class will throw exceptions if something +* objectionable happens. The exceptions are SafeIntException classes, +* which contain an enum as a code. +* +* Typical usage might be: +* +* bool foo() +* { +* SafeInt s; //note that s == 0 unless set +* +* try{ +* s *= 23; +* .... +* } +* catch(SafeIntException err) +* { +* //handle errors here +* } +* } +* +* Update for 3.0 - the exception class is now a template parameter. +* You can replace the exception class with any exception class you like. This is accomplished by: +* 1) Create a class that has the following interface: +* + template <> class YourSafeIntExceptionHandler < YourException > + { + public: + static __declspec(noreturn) void __stdcall SafeIntOnOverflow() + { + throw YourException( YourSafeIntArithmeticOverflowError ); + } + + static __declspec(noreturn) void __stdcall SafeIntOnDivZero() + { + throw YourException( YourSafeIntDivideByZeroError ); + } + }; +* +* Note that you don't have to throw C++ exceptions, you can throw Win32 exceptions, or do +* anything you like, just don't return from the call back into the code. +* +* 2) Either explicitly declare SafeInts like so: +* SafeInt< int, YourSafeIntExceptionHandler > si; +* or +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Performance: +* +* Due to the highly nested nature of this class, you can expect relatively poor +* performance in unoptimized code. In tests of optimized code vs. correct inline checks +* in native code, this class has been found to take approximately 8% more CPU time (this varies), +* most of which is due to exception handling. Solutions: +* +* 1) Compile optimized code - /Ox is best, /O2 also performs well. Interestingly, /O1 +* (optimize for size) does not work as well. +* 2) If that 8% hit is really a serious problem, walk through the code and inline the +* exact same checks as the class uses. +* 3) Some operations are more difficult than others - avoid using signed integers, and if +* possible keep them all the same size. 64-bit integers are also expensive. Mixing +* different integer sizes and types may prove expensive. Be aware that literals are +* actually ints. For best performance, cast literals to the type desired. +* +* +* Performance update +* The current version of SafeInt uses template specialization to force the compiler to invoke only the +* operator implementation needed for any given pair of types. This will dramatically improve the perf +* of debug builds. +* +* 3.0 update - not only have we maintained the specialization, there were some cases that were overly complex, +* and using some additional cases (e.g. std::int64_t and std::uint64_t) resulted in some simplification. +* Additionally, there was a lot of work done to better optimize the 64-bit multiplication. +* +* Binary Operators +* +* All of the binary operators have certain assumptions built into the class design. +* This is to ensure correctness. Notes on each class of operator follow: +* +* Arithmetic Operators (*,/,+,-,%) +* There are three possible variants: +* SafeInt< T, E > op SafeInt< T, E > +* SafeInt< T, E > op U +* U op SafeInt< T, E > +* +* The SafeInt< T, E > op SafeInt< U, E > variant is explicitly not supported, and if you try to do +* this the compiler with throw the following error: +* +* error C2593: 'operator *' is ambiguous +* +* This is because the arithmetic operators are required to return a SafeInt of some type. +* The compiler cannot know whether you'd prefer to get a type T or a type U returned. If +* you need to do this, you need to extract the value contained within one of the two using +* the casting operator. For example: +* +* SafeInt< T, E > t, result; +* SafeInt< U, E > u; +* +* result = t * (U)u; +* +* Comparison Operators +* Because each of these operators return type bool, mixing SafeInts of differing types is +* allowed. +* +* Shift Operators +* Shift operators always return the type on the left hand side of the operator. Mixed type +* operations are allowed because the return type is always known. +* +* Boolean Operators +* Like comparison operators, these overloads always return type bool, and mixed-type SafeInts +* are allowed. Additionally, specific overloads exist for type bool on both sides of the +* operator. +* +* Binary Operators +* Mixed-type operations are discouraged, however some provision has been made in order to +* enable things like: +* +* SafeInt c = 2; +* +* if(c & 0x02) +* ... +* +* The "0x02" is actually an int, and it needs to work. +* In the case of binary operations on integers smaller than 32-bit, or of mixed type, corner +* cases do exist where you could get unexpected results. In any case where SafeInt returns a different +* result than the underlying operator, it will call assert(). You should examine your code and cast things +* properly so that you are not programming with side effects. +* +* Documented issues: +* +* This header compiles correctly at /W4 using VC++ 8 (Version 14.00.50727.42) and later. +* As of this writing, I believe it will also work for VC 7.1, but not for VC 7.0 or below. +* If you need a version that will work with lower level compilers, try version 1.0.7. None +* of them work with Visual C++ 6, and gcc didn't work very well, either, though this hasn't +* been tried recently. +* +* It is strongly recommended that any code doing integer manipulation be compiled at /W4 +* - there are a number of warnings which pertain to integer manipulation enabled that are +* not enabled at /W3 (default for VC++) +* +* Perf note - postfix operators are slightly more costly than prefix operators. +* Unless you're actually assigning it to something, ++SafeInt is less expensive than SafeInt++ +* +* The comparison operator behavior in this class varies from the ANSI definition, which is +* arguably broken. As an example, consider the following: +* +* unsigned int l = 0xffffffff; +* char c = -1; +* +* if(c == l) +* printf("Why is -1 equal to 4 billion???\n"); +* +* The problem here is that c gets cast to an int, now has a value of 0xffffffff, and then gets +* cast again to an unsigned int, losing the true value. This behavior is despite the fact that +* an std::int64_t exists, and the following code will yield a different (and intuitively correct) +* answer: +* +* if((std::int64_t)c == (std::int64_t)l)) +* printf("Why is -1 equal to 4 billion???\n"); +* else +* printf("Why doesn't the compiler upcast to 64-bits when needed?\n"); +* +* Note that combinations with smaller integers won't display the problem - if you +* changed "unsigned int" above to "unsigned short", you'd get the right answer. +* +* Revision history: +* +* Oct 12, 2003 - Created +* Author - David LeBlanc - dleblanc@microsoft.com +* +* Oct 27, 2003 - fixed numerous items pointed out by michmarc and bdawson +* Dec 28, 2003 - 1.0 +* added support for mixed-type operations +* thanks to vikramh +* also fixed broken std::int64_t multiplication section +* added extended support for mixed-type operations where possible +* Jan 28, 2004 - 1.0.1 +* changed WCHAR to wchar_t +* fixed a construct in two mixed-type assignment overloads that was +* not compiling on some compilers +* Also changed name of private method to comply with standards on +* reserved names +* Thanks to Niels Dekker for the input +* Feb 12, 2004 - 1.0.2 +* Minor changes to remove dependency on Windows headers +* Consistently used std::int16_t, std::int32_t and std::int64_t to ensure +* portability +* May 10, 2004 - 1.0.3 +* Corrected bug in one case of GreaterThan +* July 22, 2004 - 1.0.4 +* Tightened logic in addition check (saving 2 instructions) +* Pulled error handler out into function to enable user-defined replacement +* Made internal type of SafeIntException an enum (as per Niels' suggestion) +* Added casts for base integer types (as per Scott Meyers' suggestion) +* Updated usage information - see important new perf notes. +* Cleaned up several const issues (more thanks to Niels) +* +* Oct 1, 2004 - 1.0.5 +* Added support for SEH exceptions instead of C++ exceptions - Win32 only +* Made handlers for DIV0 and overflows individually overridable +* Commented out the destructor - major perf gains here +* Added cast operator for type long, since long != std::int32_t +* Corrected a couple of missing const modifiers +* Fixed broken >= and <= operators for type U op SafeInt< T, E > +* Nov 5, 2004 - 1.0.6 +* Implemented new logic in binary operators to resolve issues with +* implicit casts +* Fixed casting operator because char != signed char +* Defined std::int32_t as int instead of long +* Removed unsafe SafeInt::Value method +* Re-implemented casting operator as a result of removing Value method +* Dec 1, 2004 - 1.0.7 +* Implemented specialized operators for pointer arithmetic +* Created overloads for cases of U op= SafeInt. What you do with U +* after that may be dangerous. +* Fixed bug in corner case of MixedSizeModulus +* Fixed bug in MixedSizeMultiply and MixedSizeDivision with input of 0 +* Added throw() decorations +* +* Apr 12, 2005 - 2.0 +* Extensive revisions to leverage template specialization. +* April, 2007 Extensive revisions for version 3.0 +* Nov 22, 2009 Forked from MS internal code +* Changes needed to support gcc compiler - many thanks to Niels Dekker +* for determining not just the issues, but also suggesting fixes. +* Also updating some of the header internals to be the same as the upcoming Visual Studio version. +* +* Jan 16, 2010 64-bit gcc has long == std::int64_t, which means that many of the existing 64-bit +* templates are over-specialized. This forces a redefinition of all the 64-bit +* multiplication routines to use pointers instead of references for return +* values. Also, let's use some intrinsics for x64 Microsoft compiler to +* reduce code size, and hopefully improve efficiency. +* +* June 21, 2014 Better support for clang, higher warning levels supported for all 3 primary supported + compilers (Visual Studio, clang, gcc). + Also started to converge the code base such that the public CodePlex version will + be a drop-in replacement for the Visual Studio version. + +* Feb 12, 2018 Fixed floating point bug +* Fix to allow initialization by an enum +* Add support for static_assert, make it default to fix compiler warnings from C_ASSERT on gcc, clang +* Changed throw() to noexcept + +* March, 2018 Introduced support for constexpr, both the C++11 and C++14 flavors work. The C++14 standard + allows for much more thorough usage, and should be preferred. + +* Note about code style - throughout this class, casts will be written using C-style (T), +* not C++ style static_cast< T >. This is because the class is nearly always dealing with integer +* types, and in this case static_cast and a C cast are equivalent. Given the large number of casts, +* the code is a little more readable this way. In the event a cast is needed where static_cast couldn't +* be substituted, we'll use the new templatized cast to make it explicit what the operation is doing. +* +************************************************************************************************************ +* Version 3.0 changes: +* +* 1) The exception type thrown is now replacable, and you can throw your own exception types. This should help +* those using well-developed exception classes. +* 2) The 64-bit multiplication code has had a lot of perf work done, and should be faster than 2.0. +* 3) There is now limited floating point support. You can initialize a SafeInt with a floating point type, +* and you can cast it out (or assign) to a float as well. +* 4) There is now an Align method. I noticed people use this a lot, and rarely check errors, so now you have one. +* +* Another major improvement is the addition of external functions - if you just want to check an operation, this can now happen: +* All of the following can be invoked without dealing with creating a class, or managing exceptions. This is especially handy +* for 64-bit porting, since SafeCast compiles away for a 32-bit cast from size_t to unsigned long, but checks it for 64-bit. +* +* inline bool SafeCast( const T From, U& To ) throw() +* inline bool SafeEquals( const T t, const U u ) throw() +* inline bool SafeNotEquals( const T t, const U u ) throw() +* inline bool SafeGreaterThan( const T t, const U u ) throw() +* inline bool SafeGreaterThanEquals( const T t, const U u ) throw() +* inline bool SafeLessThan( const T t, const U u ) throw() +* inline bool SafeLessThanEquals( const T t, const U u ) throw() +* inline bool SafeModulus( const T& t, const U& u, T& result ) throw() +* inline bool SafeMultiply( T t, U u, T& result ) throw() +* inline bool SafeDivide( T t, U u, T& result ) throw() +* inline bool SafeAdd( T t, U u, T& result ) throw() +* inline bool SafeSubtract( T t, U u, T& result ) throw() +* +*/ + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +namespace msl +{ + +namespace utilities +{ +#endif + +// catch these to handle errors +// Currently implemented code values: +// ERROR_ARITHMETIC_OVERFLOW +// EXCEPTION_INT_DIVIDE_BY_ZERO +enum SafeIntError +{ + SafeIntNoError = 0, + SafeIntArithmeticOverflow, + SafeIntDivideByZero +}; + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +} // utilities +} // msl +#endif + + +/* +* Error handler classes +* Using classes to deal with exceptions is going to allow the most +* flexibility, and we can mix different error handlers in the same project +* or even the same file. It isn't advisable to do this in the same function +* because a SafeInt< int, MyExceptionHandler > isn't the same thing as +* SafeInt< int, YourExceptionHander >. +* If for some reason you have to translate between the two, cast one of them back to its +* native type. +* +* To use your own exception class with SafeInt, first create your exception class, +* which may look something like the SafeIntException class below. The second step is to +* create a template specialization that implements SafeIntOnOverflow and SafeIntOnDivZero. +* For example: +* +* template <> class SafeIntExceptionHandler < YourExceptionClass > +* { +* static __declspec(noreturn) void __stdcall SafeIntOnOverflow() +* { +* throw YourExceptionClass( EXCEPTION_INT_OVERFLOW ); +* } +* +* static __declspec(noreturn) void __stdcall SafeIntOnDivZero() +* { +* throw YourExceptionClass( EXCEPTION_INT_DIVIDE_BY_ZERO ); +* } +* }; +* +* typedef SafeIntExceptionHandler < YourExceptionClass > YourSafeIntExceptionHandler +* You'd then declare your SafeInt objects like this: +* SafeInt< int, YourSafeIntExceptionHandler > +* +* Unfortunately, there is no such thing as partial template specialization in typedef +* statements, so you have three options if you find this cumbersome: +* +* 1) Create a holder class: +* +* template < typename T > +* class MySafeInt +* { +* public: +* SafeInt< T, MyExceptionClass> si; +* }; +* +* You'd then declare an instance like so: +* MySafeInt< int > i; +* +* You'd lose handy things like initialization - it would have to be initialized as: +* +* i.si = 0; +* +* 2) You could create a typedef for every int type you deal with: +* +* typedef SafeInt< int, MyExceptionClass > MySafeInt; +* typedef SafeInt< char, MyExceptionClass > MySafeChar; +* +* and so on. The second approach is probably more usable, and will just drop into code +* better, which is the original intent of the SafeInt class. +* +* 3) If you're going to consistently use a different class to handle your exceptions, +* you can override the default typedef like so: +* +* #define SafeIntDefaultExceptionHandler YourSafeIntExceptionHandler +* +* Overall, this is probably the best approach. +* */ + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +namespace msl +{ + +namespace utilities +{ +#endif + +#if defined SAFEINT_ASSERT_ON_EXCEPTION + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW { SAFEINT_ASSERT(false); } +#else + inline void SafeIntExceptionAssert() SAFEINT_NOTHROW {} +#endif + +// Note - removed weak annotation on class due to gcc complaints +// This was the only place in the file that used it, need to better understand +// whether it was put there correctly in the first place + +class SAFEINT_VISIBLE SafeIntException +{ +public: + _CONSTEXPR11 SafeIntException( SafeIntError code = SafeIntNoError) SAFEINT_NOTHROW : m_code(code) + { + } + SafeIntError m_code; +}; + +namespace SafeIntInternal +{ + // Visual Studio version of SafeInt provides for two possible error + // handlers: + // SafeIntErrorPolicy_SafeIntException - C++ exception, default if not otherwise defined + // SafeIntErrorPolicy_InvalidParameter - Calls fail fast (Windows-specific), bypasses any exception handlers, + // exits the app with a crash + template < typename E > class SafeIntExceptionHandler; + + template <> class SafeIntExceptionHandler < SafeIntException > + { + public: + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntArithmeticOverflow ); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() + { + SafeIntExceptionAssert(); + throw SafeIntException( SafeIntDivideByZero ); + } + }; + + class SafeInt_InvalidParameter + { + public: + static SAFEINT_NORETURN void SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Arithmetic Overflow"); + } + + static SAFEINT_NORETURN void SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + _CRT_SECURE_INVALID_PARAMETER("SafeInt Divide By Zero"); + } + }; + +#if defined _WINDOWS_ + + class SafeIntWin32ExceptionHandler + { + public: + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnOverflow() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_OVERFLOW), EXCEPTION_NONCONTINUABLE, 0, 0); + } + + static SAFEINT_NORETURN void SAFEINT_STDCALL SafeIntOnDivZero() SAFEINT_NOTHROW + { + SafeIntExceptionAssert(); + RaiseException( static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), EXCEPTION_NONCONTINUABLE, 0, 0); + } + }; + +#endif + +} // namespace SafeIntInternal + +// both of these have cross-platform support +typedef SafeIntInternal::SafeIntExceptionHandler < SafeIntException > CPlusPlusExceptionHandler; +typedef SafeIntInternal::SafeInt_InvalidParameter InvalidParameterExceptionHandler; + +// This exception handler is no longer recommended, but is left here in order not to break existing users +#if defined _WINDOWS_ +typedef SafeIntInternal::SafeIntWin32ExceptionHandler Win32ExceptionHandler; +#endif + +// For Visual Studio compatibility +#if defined VISUAL_STUDIO_SAFEINT_COMPAT + typedef CPlusPlusExceptionHandler SafeIntErrorPolicy_SafeIntException; + typedef InvalidParameterExceptionHandler SafeIntErrorPolicy_InvalidParameter; +#endif + +// If the user hasn't defined a default exception handler, +// define one now, depending on whether they would like Win32 or C++ exceptions + +// This library will use conditional noexcept soon, but not in this release +// Some users might mix exception handlers, which is not advised, but is supported +#if !defined SafeIntDefaultExceptionHandler + #if defined SAFEINT_RAISE_EXCEPTION + #if !defined _WINDOWS_ + #error Include windows.h in order to use Win32 exceptions + #endif + + #define SafeIntDefaultExceptionHandler Win32ExceptionHandler + #elif defined SAFEINT_FAILFAST + #define SafeIntDefaultExceptionHandler InvalidParameterExceptionHandler + #else + #define SafeIntDefaultExceptionHandler CPlusPlusExceptionHandler + #if !defined SAFEINT_EXCEPTION_HANDLER_CPP + #define SAFEINT_EXCEPTION_HANDLER_CPP 1 + #endif + #endif +#endif + +#if !defined SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_EXCEPTION_HANDLER_CPP 0 +#endif + +// If an error handler is chosen other than C++ exceptions, such as Win32 exceptions, fail fast, +// or abort, then all methods become no throw. Some teams track throw() annotations closely, +// and the following option provides for this. +#if SAFEINT_EXCEPTION_HANDLER_CPP +#define SAFEINT_CPP_THROW +#else +#define SAFEINT_CPP_THROW SAFEINT_NOTHROW +#endif + +namespace safeint_internal +{ + // If we have support for std, then we can do this easily, and detect enums as well + template < typename T > class numeric_type; + + // Continue to special case bool + template <> class numeric_type { public: enum { isBool = true, isInt = false }; }; + template < typename T > class numeric_type + { + public: + enum + { + isBool = false, // We specialized out a bool + // If it is an enum, then consider it an int type + // This does allow someone to make a SafeInt from an enum type, which is not recommended, + // but it also allows someone to add an enum value to a SafeInt, which is handy. + isInt = std::is_integral::value || std::is_enum::value, + isEnum = std::is_enum::value + }; + }; + + template < typename T > class int_traits + { + public: + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + + enum + { + is64Bit = (sizeof(T) == 8), + is32Bit = (sizeof(T) == 4), + is16Bit = (sizeof(T) == 2), + is8Bit = (sizeof(T) == 1), + isLT32Bit = (sizeof(T) < 4), + isLT64Bit = (sizeof(T) < 8), + isInt8 = (sizeof(T) == 1 && std::numeric_limits::is_signed), + isUint8 = (sizeof(T) == 1 && !std::numeric_limits::is_signed), + isInt16 = (sizeof(T) == 2 && std::numeric_limits::is_signed), + isUint16 = (sizeof(T) == 2 && !std::numeric_limits::is_signed), + isInt32 = (sizeof(T) == 4 && std::numeric_limits::is_signed), + isUint32 = (sizeof(T) == 4 && !std::numeric_limits::is_signed), + isInt64 = (sizeof(T) == 8 && std::numeric_limits::is_signed), + isUint64 = (sizeof(T) == 8 && !std::numeric_limits::is_signed), + bitCount = (sizeof(T) * 8), + isBool = ((T)2 == (T)1) + }; + }; + + template < typename T, typename U > class type_compare + { + public: + enum + { + isBothSigned = (std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed), + isBothUnsigned = (!std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed), + isLikeSigned = ((bool)(std::numeric_limits< T >::is_signed) == (bool)(std::numeric_limits< U >::is_signed)), + isCastOK = ((isLikeSigned && sizeof(T) >= sizeof(U)) || + (std::numeric_limits< T >::is_signed && sizeof(T) > sizeof(U))), + isBothLT32Bit = (safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::isLT32Bit), + isBothLT64Bit = (safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isLT64Bit) + }; + }; + +} +//all of the arithmetic operators can be solved by the same code within +//each of these regions without resorting to compile-time constant conditionals +//most operators collapse the problem into less than the 22 zones, but this is used +//as the first cut +//using this also helps ensure that we handle all of the possible cases correctly + +template < typename T, typename U > class IntRegion +{ +public: + enum + { + //unsigned-unsigned zone + IntZone_UintLT32_UintLT32 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::type_compare< T,U >::isBothLT32Bit, + IntZone_Uint32_UintLT64 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::is32Bit && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_UintLT32_Uint32 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::is32Bit, + IntZone_Uint64_Uint = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::is64Bit, + IntZone_UintLT64_Uint64 = safeint_internal::type_compare< T,U >::isBothUnsigned && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::is64Bit, + //unsigned-signed + IntZone_UintLT32_IntLT32 = !std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed && safeint_internal::type_compare< T,U >::isBothLT32Bit, + IntZone_Uint32_IntLT64 = safeint_internal::int_traits< T >::isUint32 && std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_UintLT32_Int32 = !std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::isInt32, + IntZone_Uint64_Int = safeint_internal::int_traits< T >::isUint64 && std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_UintLT64_Int64 = !std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isInt64, + IntZone_Uint64_Int64 = safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64, + //signed-signed + IntZone_IntLT32_IntLT32 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::type_compare< T, U >::isBothLT32Bit, + IntZone_Int32_IntLT64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::is32Bit && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_IntLT32_Int32 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isLT32Bit && safeint_internal::int_traits< U >::is32Bit, + IntZone_Int64_Int64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isInt64, + IntZone_Int64_Int = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::is64Bit && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_IntLT64_Int64 = safeint_internal::type_compare< T,U >::isBothSigned && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::is64Bit, + //signed-unsigned + IntZone_IntLT32_UintLT32 = std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed && safeint_internal::type_compare< T,U >::isBothLT32Bit, + IntZone_Int32_UintLT32 = safeint_internal::int_traits< T >::isInt32 && !std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT32Bit, + IntZone_IntLT64_Uint32 = std::numeric_limits< T >::is_signed && safeint_internal::int_traits< T >::isLT64Bit && safeint_internal::int_traits< U >::isUint32, + IntZone_Int64_UintLT64 = safeint_internal::int_traits< T >::isInt64 && !std::numeric_limits< U >::is_signed && safeint_internal::int_traits< U >::isLT64Bit, + IntZone_Int_Uint64 = std::numeric_limits< T >::is_signed && safeint_internal::int_traits< U >::isUint64 && safeint_internal::int_traits< T >::isLT64Bit, + IntZone_Int64_Uint64 = safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64 + }; +}; + +// In all of the following functions, we have two versions +// One for SafeInt, which throws C++ (or possibly SEH) exceptions +// The non-throwing versions are for use by the helper functions that return success and failure. +// Some of the non-throwing functions are not used, but are maintained for completeness. + +// There's no real alternative to duplicating logic, but keeping the two versions +// immediately next to one another will help reduce problems + +// useful function to help with getting the magnitude of a negative number +enum AbsMethod +{ + AbsMethodInt, + AbsMethodInt64, + AbsMethodNoop +}; + +template < typename T > +class GetAbsMethod +{ +public: + enum + { + method = safeint_internal::int_traits< T >::isLT64Bit && std::numeric_limits< T >::is_signed ? AbsMethodInt : + safeint_internal::int_traits< T >::isInt64 ? AbsMethodInt64 : AbsMethodNoop + }; +}; + +// let's go ahead and hard-code a dependency on the +// representation of negative numbers to keep compilers from getting overly +// happy with optimizing away things like -MIN_INT. +template < typename T, int > class AbsValueHelper; + +template < typename T > class AbsValueHelper < T, AbsMethodInt> +{ +public: + _CONSTEXPR14 static std::uint32_t Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(std::uint32_t)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodInt64 > +{ +public: + _CONSTEXPR14 static std::uint64_t Abs( T t ) SAFEINT_NOTHROW + { + SAFEINT_ASSERT( t < 0 ); + return ~(std::uint64_t)t + 1; + } +}; + +template < typename T > class AbsValueHelper < T, AbsMethodNoop > +{ +public: + _CONSTEXPR14 static T Abs( T t ) SAFEINT_NOTHROW + { + // Why are you calling Abs on an unsigned number ??? + SAFEINT_ASSERT( false ); + return t; + } +}; + +// Helper classes to work keep compilers from +// optimizing away negation +template < typename T > class SignedNegation; + +template <> +class SignedNegation +{ +public: + _CONSTEXPR11 static std::int32_t Value(std::uint64_t in) SAFEINT_NOTHROW + { + return (std::int32_t)(~(std::uint32_t)in + 1); + } + + _CONSTEXPR11 static std::int32_t Value(std::uint32_t in) SAFEINT_NOTHROW + { + return (std::int32_t)(~in + 1); + } +}; + +template <> +class SignedNegation +{ +public: + _CONSTEXPR11 static std::int64_t Value(std::uint64_t in) SAFEINT_NOTHROW + { + return (std::int64_t)(~in + 1); + } +}; + +template < typename T, bool > class NegationHelper; +// Previous versions had an assert that the type being negated was 32-bit or higher +// In retrospect, this seems like something to just document +// Negation will normally upcast to int +// For example -(unsigned short)0xffff == (int)0xffff0001 +// This class will retain the type, and will truncate, which may not be what +// you wanted +// If you want normal operator casting behavior, do this: +// SafeInt ss = 0xffff; +// then: +// -(SafeInt(ss)) +// will then emit a signed int with the correct value and bitfield + +// Note, unlike all of the other helper classes, the non-throwing negation +// doesn't make sense, isn't exposed or tested, so omit it + +template < typename T > class NegationHelper // Signed +{ +public: + template + _CONSTEXPR14 static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { + // corner case + if( t != std::numeric_limits::min() ) + { + // cast prevents unneeded checks in the case of small ints + return -t; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T > class NegationHelper // unsigned +{ +public: + template + _CONSTEXPR14 static T NegativeThrow( T t ) SAFEINT_CPP_THROW + { +#if defined SAFEINT_DISALLOW_UNSIGNED_NEGATION + static_assert( sizeof(T) == 0, "Unsigned negation is unsupported" ); +#endif + // This may not be the most efficient approach, but you shouldn't be doing this + return (T)SignedNegation::Value(t); + } + +}; + +//core logic to determine casting behavior +enum CastMethod +{ + CastOK = 0, + CastCheckLTZero, + CastCheckGTMax, + CastCheckSafeIntMinMaxUnsigned, + CastCheckSafeIntMinMaxSigned, + CastToFloat, + CastFromFloat, + CastToBool, + CastFromBool, + CastFromEnum +}; + + +template < typename ToType, typename FromType > +class GetCastMethod +{ +public: + enum + { + method = ( safeint_internal::numeric_type::isEnum ) ? CastFromEnum : + ( safeint_internal::int_traits< FromType >::isBool && + !safeint_internal::int_traits< ToType >::isBool ) ? CastFromBool : + + ( !safeint_internal::int_traits< FromType >::isBool && + safeint_internal::int_traits< ToType >::isBool ) ? CastToBool : + + ( safeint_internal::type_compare< ToType, FromType >::isCastOK ) ? CastOK : + + ( ( std::numeric_limits< ToType >::is_signed && + !std::numeric_limits< FromType >::is_signed && + sizeof( FromType ) >= sizeof( ToType ) ) || + ( safeint_internal::type_compare< ToType, FromType >::isBothUnsigned && + sizeof( FromType ) > sizeof( ToType ) ) ) ? CastCheckGTMax : + + ( !std::numeric_limits< ToType >::is_signed && + std::numeric_limits< FromType >::is_signed && + sizeof( ToType ) >= sizeof( FromType ) ) ? CastCheckLTZero : + + ( !std::numeric_limits< ToType >::is_signed ) ? CastCheckSafeIntMinMaxUnsigned + : CastCheckSafeIntMinMaxSigned + }; +}; + +template < typename FromType > class GetCastMethod < float, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename FromType > class GetCastMethod < long double, FromType > +{ +public: + enum{ method = CastOK }; +}; + +template < typename ToType > class GetCastMethod < ToType, float > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename ToType > class GetCastMethod < ToType, long double > +{ +public: + enum{ method = CastFromFloat }; +}; + +template < typename T, typename U, int > class SafeCastHelper; + +template < typename T, typename U > class SafeCastHelper < T, U, CastOK > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + t = (T)u; + } +}; + +template class float_cast_helper; + +template class float_cast_helper // Unsigned case +{ +public: + _CONSTEXPR14 static bool Test(double d) + { + const std::uint64_t signifDouble = 0x1fffffffffffff; + + // Anything larger than this either is larger than 2^64-1, or cannot be represented by a double + const std::uint64_t maxUnsignedDouble = signifDouble << 11; + + // There is the possibility of both negative and positive zero, + // but we'll allow either, since (-0.0 < 0) == false + // if we wanted to change that, then use the signbit() macro + if (d < 0 || d > static_cast(maxUnsignedDouble)) + return false; + + // The input can now safely be cast to an unsigned long long + if (static_cast(d) > std::numeric_limits::max()) + return false; + + return true; + } +}; + +template class float_cast_helper // Signed case +{ +public: + _CONSTEXPR14 static bool Test(double d) + { + const std::uint64_t signifDouble = 0x1fffffffffffff; + // This has to fit in 2^63-1 + const std::uint64_t maxSignedDouble = signifDouble << 10; + // The smallest signed long long is easier + const std::int64_t minSignedDouble = static_cast(0x8000000000000000); + + if (d < static_cast(minSignedDouble) || d > static_cast(maxSignedDouble)) + return false; + + // And now cast to long long, and check against min and max for this type + std::int64_t test = static_cast(d); + if ((std::int64_t)test < (std::int64_t)std::numeric_limits::min() || (std::int64_t)test >(std::int64_t)std::numeric_limits::max()) + return false; + + return true; + } +}; + +// special case floats and doubles +template < typename T, typename U > class SafeCastHelper < T, U, CastFromFloat > +{ +public: + + static bool CheckFloatingPointCast(double d) + { + // A double can hold at most 53 bits of the value + // 53 bits is: + bool fValid = false; + + switch (std::fpclassify(d)) + { + case FP_NORMAL: // A positive or negative normalized non - zero value + case FP_SUBNORMAL: // A positive or negative denormalized value + case FP_ZERO: // A positive or negative zero value + fValid = true; + break; + + case FP_NAN: // A quiet, signaling, or indeterminate NaN + case FP_INFINITE: // A positive or negative infinity + default: + fValid = false; + break; + } + + if (!fValid) + return false; + + return float_cast_helper< T, !std::numeric_limits< T >::is_signed >::Test(d); + } + + static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if(CheckFloatingPointCast(u)) + { + t = (T)u; + return true; + } + return false; + } + + template < typename E > + static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if (CheckFloatingPointCast(u)) + { + t = (T)u; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastFromEnum > +{ +public: + _CONSTEXPR14 static bool Cast(U u, T& t) SAFEINT_NOTHROW + { + return SafeCastHelper< T, int, GetCastMethod< T, int >::method >::Cast(static_cast(u), t); + } + + template < typename E > + _CONSTEXPR14 static void CastThrow(U u, T& t) SAFEINT_CPP_THROW + { + SafeCastHelper< T, int, GetCastMethod< T, int >::method >::template CastThrow< E >(static_cast(u), t); + } +}; + +// Match on any method where a bool is cast to type T +template < typename T > class SafeCastHelper < T, bool, CastFromBool > +{ +public: + _CONSTEXPR14 static bool Cast( bool b, T& t ) SAFEINT_NOTHROW + { + t = (T)( b ? 1 : 0 ); + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + t = (T)( b ? 1 : 0 ); + } +}; + +template < typename T > class SafeCastHelper < bool, T, CastToBool > +{ +public: + _CONSTEXPR14 static bool Cast( T t, bool& b ) SAFEINT_NOTHROW + { + b = !!t; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( bool b, T& t ) SAFEINT_CPP_THROW + { + b = !!t; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckLTZero > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckGTMax > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + if( u > (U)std::numeric_limits::max() ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + if( u > (U)std::numeric_limits::max() ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxUnsigned > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // U is signed - T could be either signed or unsigned + if( u > std::numeric_limits::max() || u < 0 ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + // U is signed - T could be either signed or unsigned + if( u > std::numeric_limits::max() || u < 0 ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +template < typename T, typename U > class SafeCastHelper < T, U, CastCheckSafeIntMinMaxSigned > +{ +public: + _CONSTEXPR14 static bool Cast( U u, T& t ) SAFEINT_NOTHROW + { + // T, U are signed + if( u > std::numeric_limits::max() || u < std::numeric_limits::min() ) + return false; + + t = (T)u; + return true; + } + + template < typename E > + _CONSTEXPR14 static void CastThrow( U u, T& t ) SAFEINT_CPP_THROW + { + //T, U are signed + if( u > std::numeric_limits::max() || u < std::numeric_limits::min() ) + E::SafeIntOnOverflow(); + + t = (T)u; + } +}; + +//core logic to determine whether a comparison is valid, or needs special treatment +enum ComparisonMethod +{ + ComparisonMethod_Ok = 0, + ComparisonMethod_CastInt, + ComparisonMethod_CastInt64, + ComparisonMethod_UnsignedT, + ComparisonMethod_UnsignedU +}; + + // Note - the standard is arguably broken in the case of some integer + // conversion operations + // For example, signed char a = -1 = 0xff + // unsigned int b = 0xffffffff + // If you then test if a < b, a value-preserving cast + // is made, and you're essentially testing + // (unsigned int)a < b == false + // + // I do not think this makes sense - if you perform + // a cast to an std::int64_t, which can clearly preserve both value and signedness + // then you get a different and intuitively correct answer + // IMHO, -1 should be less than 4 billion + // If you prefer to retain the ANSI standard behavior + // insert #define ANSI_CONVERSIONS into your source + // Behavior differences occur in the following cases: + // 8, 16, and 32-bit signed int, unsigned 32-bit int + // any signed int, unsigned 64-bit int + // Note - the signed int must be negative to show the problem + +template < typename T, typename U > +class ValidComparison +{ +public: + enum + { + method = ( ( safeint_internal::type_compare< T, U >::isLikeSigned ) ? ComparisonMethod_Ok : + ( ( std::numeric_limits< T >::is_signed && sizeof(T) < 8 && sizeof(U) < 4 ) || + ( std::numeric_limits< U >::is_signed && sizeof(T) < 4 && sizeof(U) < 8 ) ) ? ComparisonMethod_CastInt : + ( ( std::numeric_limits< T >::is_signed && sizeof(U) < 8 ) || + ( std::numeric_limits< U >::is_signed && sizeof(T) < 8 ) ) ? ComparisonMethod_CastInt64 : + ( !std::numeric_limits< T >::is_signed ) ? ComparisonMethod_UnsignedT : + ComparisonMethod_UnsignedU ) + }; +}; + +template class EqualityTest; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_Ok > +{ +public: + _CONSTEXPR11 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( t == u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt > +{ +public: + _CONSTEXPR11 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t == (int)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + _CONSTEXPR11 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW { return ( (std::int64_t)t == (std::int64_t)u ); } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + _CONSTEXPR14 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return false; + + //else safe to cast to type T + return ( t == (T)u ); + } +}; + +template < typename T, typename U > class EqualityTest< T, U, ComparisonMethod_UnsignedU> +{ +public: + _CONSTEXPR14 static bool IsEquals( const T t, const U u ) SAFEINT_NOTHROW + { + //one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + //else safe to cast to type U + return ( (U)t == u ); + } +}; + +template class GreaterThanTest; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_Ok > +{ +public: + _CONSTEXPR11 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( t > u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt > +{ +public: + _CONSTEXPR11 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (int)t > (int)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_CastInt64 > +{ +public: + _CONSTEXPR11 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW { return ( (std::int64_t)t > (std::int64_t)u ); } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedT > +{ +public: + _CONSTEXPR14 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( u < 0 ) + return true; + + // else safe to cast to type T + return ( t > (T)u ); + } +}; + +template < typename T, typename U > class GreaterThanTest< T, U, ComparisonMethod_UnsignedU > +{ +public: + _CONSTEXPR14 static bool GreaterThan( const T t, const U u ) SAFEINT_NOTHROW + { + // one operand is 32 or 64-bit unsigned, and the other is signed and the same size or smaller + if( t < 0 ) + return false; + + // else safe to cast to type U + return ( (U)t > u ); + } +}; + +// Modulus is simpler than comparison, but follows much the same logic +// using this set of functions, it can't fail except in a div 0 situation +template class ModulusHelper; + +template class mod_corner_case; + +template class mod_corner_case // signed +{ +public: + _CONSTEXPR14 static bool is_undefined(U u) + { + return (u == -1); + } +}; + +template class mod_corner_case // unsigned +{ +public: + _CONSTEXPR14 static bool is_undefined(U) + { + return false; + } +}; + +template class ModulusHelper +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if(mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return SafeIntNoError; + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return; + } + + result = (T)(t % u); + } +}; + +template class ModulusHelper +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return SafeIntNoError; + } + + result = (T)(t % u); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return; + } + + result = (T)(t % u); + } +}; + +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_CastInt64> +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //trap corner case + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return SafeIntNoError; + } + + result = (T)((std::int64_t)t % (std::int64_t)u); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + if (mod_corner_case::is_signed >::is_undefined(u)) + { + result = 0; + return; + } + + result = (T)((std::int64_t)t % (std::int64_t)u); + } +}; + +// T is std::uint64_t, U is any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedT> +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + // u could be negative - if so, need to convert to positive + // casts below are always safe due to the way modulus works + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs(u)); + else + result = (T)(t % u); + + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + // u could be negative - if so, need to convert to positive + if(u < 0) + result = (T)(t % AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u )); + else + result = (T)(t % u); + } +}; + +// U is std::uint64_t, T any signed int +template < typename T, typename U > class ModulusHelper< T, U, ComparisonMethod_UnsignedU> +{ +public: + _CONSTEXPR14 static SafeIntError Modulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if(u == 0) + return SafeIntDivideByZero; + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1 ); + else + result = (T)((T)t % u); + + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void ModulusThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + E::SafeIntOnDivZero(); + + //t could be negative - if so, need to convert to positive + if(t < 0) + result = (T)( ~( AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( t ) % u ) + 1); + else + result = (T)( (T)t % u ); + } +}; + +//core logic to determine method to check multiplication +enum MultiplicationState +{ + MultiplicationState_CastInt = 0, // One or both signed, smaller than 32-bit + MultiplicationState_CastInt64, // One or both signed, smaller than 64-bit + MultiplicationState_CastUint, // Both are unsigned, smaller than 32-bit + MultiplicationState_CastUint64, // Both are unsigned, both 32-bit or smaller + MultiplicationState_Uint64Uint, // Both are unsigned, lhs 64-bit, rhs 32-bit or smaller + MultiplicationState_Uint64Uint64, // Both are unsigned int64 + MultiplicationState_Uint64Int, // lhs is unsigned int64, rhs int32 + MultiplicationState_Uint64Int64, // lhs is unsigned int64, rhs signed int64 + MultiplicationState_UintUint64, // Both are unsigned, lhs 32-bit or smaller, rhs 64-bit + MultiplicationState_UintInt64, // lhs unsigned 32-bit or less, rhs int64 + MultiplicationState_Int64Uint, // lhs int64, rhs unsigned int32 + MultiplicationState_Int64Int64, // lhs int64, rhs int64 + MultiplicationState_Int64Int, // lhs int64, rhs int32 + MultiplicationState_IntUint64, // lhs int, rhs unsigned int64 + MultiplicationState_IntInt64, // lhs int, rhs int64 + MultiplicationState_Int64Uint64, // lhs int64, rhs uint64 + MultiplicationState_Error +}; + +template < typename T, typename U > +class MultiplicationMethod +{ +public: + enum + { + // unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? MultiplicationState_CastUint : + (IntRegion< T,U >::IntZone_Uint32_UintLT64 || + IntRegion< T,U >::IntZone_UintLT32_Uint32) ? MultiplicationState_CastUint64 : + safeint_internal::type_compare< T,U >::isBothUnsigned && + safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isUint64 ? MultiplicationState_Uint64Uint64 : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? MultiplicationState_Uint64Uint : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? MultiplicationState_UintUint64 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int) ? MultiplicationState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? MultiplicationState_UintInt64 : + (IntRegion< T,U >::IntZone_Uint64_Int64) ? MultiplicationState_Uint64Int64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_Int64) ? MultiplicationState_Int64Int64 : + (IntRegion< T,U >::IntZone_Int64_Int) ? MultiplicationState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? MultiplicationState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? MultiplicationState_CastInt : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? MultiplicationState_CastInt64 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? MultiplicationState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? MultiplicationState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64 ? MultiplicationState_Int64Uint64 : + MultiplicationState_Error ) ) + }; +}; + +template class MultiplicationHelper; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt> +{ +public: + //accepts signed, both less than 32-bit + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + int tmp = t * u; + + if( tmp > std::numeric_limits::max() || tmp < std::numeric_limits::min() ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + int tmp = t * u; + + if( tmp > std::numeric_limits::max() || tmp < std::numeric_limits::min() ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint > +{ +public: + //accepts unsigned, both less than 32-bit + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + unsigned int tmp = (unsigned int)(t * u); + + if( tmp > std::numeric_limits::max() ) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + unsigned int tmp = (unsigned int)( t * u ); + + if( tmp > std::numeric_limits::max() ) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastInt64> +{ +public: + //mixed signed or both signed where at least one argument is 32-bit, and both a 32-bit or less + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + std::int64_t tmp = (std::int64_t)t * (std::int64_t)u; + + if(tmp > (std::int64_t)std::numeric_limits::max() || tmp < (std::int64_t)std::numeric_limits::min()) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + std::int64_t tmp = (std::int64_t)t * (std::int64_t)u; + + if(tmp > (std::int64_t)std::numeric_limits::max() || tmp < (std::int64_t)std::numeric_limits::min()) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_CastUint64> +{ +public: + //both unsigned where at least one argument is 32-bit, and both are 32-bit or less + _CONSTEXPR14 static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + std::uint64_t tmp = (std::uint64_t)t * (std::uint64_t)u; + + if(tmp > (std::uint64_t)std::numeric_limits::max()) + return false; + + ret = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + std::uint64_t tmp = (std::uint64_t)t * (std::uint64_t)u; + + if(tmp > (std::uint64_t)std::numeric_limits::max()) + E::SafeIntOnOverflow(); + + ret = (T)tmp; + } +}; + +// T = left arg and return type +// U = right arg +template < typename T, typename U > class LargeIntRegMultiply; + +#if SAFEINT_USE_INTRINSICS +// As usual, unsigned is easy +inline bool IntrinsicMultiplyUint64( const std::uint64_t& a, const std::uint64_t& b, std::uint64_t* pRet ) SAFEINT_NOTHROW +{ + std::uint64_t ulHigh = 0; + *pRet = _umul128(a , b, &ulHigh); + return ulHigh == 0; +} + +// Signed, is not so easy +inline bool IntrinsicMultiplyInt64( const std::int64_t& a, const std::int64_t& b, std::int64_t* pRet ) SAFEINT_NOTHROW +{ + std::int64_t llHigh = 0; + *pRet = _mul128(a , b, &llHigh); + + // Now we need to figure out what we expect + // If llHigh is 0, then treat *pRet as unsigned + // If llHigh is < 0, then treat *pRet as signed + + if( (a ^ b) < 0 ) + { + // Negative result expected + if( llHigh == -1 && *pRet < 0 || + llHigh == 0 && *pRet == 0 ) + { + // Everything is within range + return true; + } + } + else + { + // Result should be positive + // Check for overflow + if( llHigh == 0 && (std::uint64_t)*pRet <= (std::uint64_t)std::numeric_limits::max() ) + return true; + } + return false; +} + +#endif + +template<> class LargeIntRegMultiply< std::uint64_t, std::uint64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, const std::uint64_t& b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, b, pRet ); +#else + std::uint32_t aHigh = 0, aLow = 0, bHigh = 0, bLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (std::uint64_t)aLow * (std::uint64_t)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)bLow; + } + } + else + { + return false; + } + + if(*pRet != 0) + { + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)bLow; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)bLow; + return true; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, const std::uint64_t& b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + std::uint32_t aHigh = 0, aLow = 0, bHigh = 0, bLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // Note - same approach applies for 128 bit math on a 64-bit system + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(aHigh == 0) + { + if(bHigh != 0) + { + *pRet = (std::uint64_t)aLow * (std::uint64_t)bHigh; + } + } + else if(bHigh == 0) + { + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)bLow; + } + } + else + { + E::SafeIntOnOverflow(); + } + + if(*pRet != 0) + { + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)bLow; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)bLow; +#endif + } +}; + +template<> class LargeIntRegMultiply< std::uint64_t, std::uint32_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, std::uint32_t b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ); +#else + std::uint32_t aHigh = 0, aLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)b; + + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + return false; + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)b; + *pRet += tmp; + + if(*pRet < tmp) + return false; + + return true; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)b; + return true; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, std::uint32_t b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + std::uint32_t aHigh = 0, aLow = 0; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * b + // => (aHigh * b * 2^32) + (aLow * b) + + aHigh = (std::uint32_t)(a >> 32); + aLow = (std::uint32_t)a; + + *pRet = 0; + + if(aHigh != 0) + { + *pRet = (std::uint64_t)aHigh * (std::uint64_t)b; + + std::uint64_t tmp = 0; + + if((std::uint32_t)(*pRet >> 32) != 0) + E::SafeIntOnOverflow(); + + *pRet <<= 32; + tmp = (std::uint64_t)aLow * (std::uint64_t)b; + *pRet += tmp; + + if(*pRet < tmp) + E::SafeIntOnOverflow(); + + return; + } + + *pRet = (std::uint64_t)aLow * (std::uint64_t)b; + return; +#endif + } +}; + +template<> class LargeIntRegMultiply< std::uint64_t, std::int32_t > +{ +public: + // Intrinsic not needed + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, std::int32_t b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ); +#else + return LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply(a, (std::uint32_t)b, pRet); +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, std::int32_t b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( a, (std::uint32_t)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::uint64_t, std::int64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::uint64_t& a, std::int64_t b, std::uint64_t* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ); +#else + return LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply(a, (std::uint64_t)b, pRet); +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::uint64_t& a, std::int64_t b, std::uint64_t* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyUint64( a, (std::uint64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + LargeIntRegMultiply< std::uint64_t, std::uint64_t >::template RegMultiplyThrow< E >( a, (std::uint64_t)b, pRet ); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int32_t, std::uint64_t > +{ +public: + // Devolves into ordinary 64-bit calculation + _CONSTEXPR14 static bool RegMultiply( std::int32_t a, const std::uint64_t& b, std::int32_t* pRet ) SAFEINT_NOTHROW + { + std::uint32_t bHigh = 0, bLow = 0; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + // aHigh == 0 implies: + // ( aLow * bHigh * 2^32 ) + ( aLow + bLow ) + // If the first part is != 0, fail + + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + return false; + + if( a < 0 ) + { + + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + fIsNegative = true; + } + + std::uint64_t tmp = (std::uint32_t)a * (std::uint64_t)bLow; + + if( !fIsNegative ) + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return true; + } + } + else + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max()+1 ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void RegMultiplyThrow( std::int32_t a, const std::uint64_t& b, std::int32_t* pRet ) SAFEINT_CPP_THROW + { + std::uint32_t bHigh = 0, bLow = 0; + bool fIsNegative = false; + + // Consider that a*b can be broken up into: + // (aHigh * 2^32 + aLow) * (bHigh * 2^32 + bLow) + // => (aHigh * bHigh * 2^64) + (aLow * bHigh * 2^32) + (aHigh * bLow * 2^32) + (aLow * bLow) + + bHigh = (std::uint32_t)(b >> 32); + bLow = (std::uint32_t)b; + + *pRet = 0; + + if(bHigh != 0 && a != 0) + E::SafeIntOnOverflow(); + + if( a < 0 ) + { + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + fIsNegative = true; + } + + std::uint64_t tmp = (std::uint32_t)a * (std::uint64_t)bLow; + + if( !fIsNegative ) + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return; + } + } + else + { + if( tmp <= (std::uint64_t)std::numeric_limits< std::int32_t >::max()+1 ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template<> class LargeIntRegMultiply< std::uint32_t, std::uint64_t > +{ +public: + // Becomes ordinary 64-bit multiplication, intrinsic not needed + _CONSTEXPR14 static bool RegMultiply( std::uint32_t a, const std::uint64_t& b, std::uint32_t* pRet ) SAFEINT_NOTHROW + { + // Consider that a*b can be broken up into: + // (bHigh * 2^32 + bLow) * a + // => (bHigh * a * 2^32) + (bLow * a) + // In this case, the result must fit into 32-bits + // If bHigh != 0 && a != 0, immediate error. + + if( (std::uint32_t)(b >> 32) != 0 && a != 0 ) + return false; + + std::uint64_t tmp = b * (std::uint64_t)a; + + if( (std::uint32_t)(tmp >> 32) != 0 ) // overflow + return false; + + *pRet = (std::uint32_t)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void RegMultiplyThrow( std::uint32_t a, const std::uint64_t& b, std::uint32_t* pRet ) SAFEINT_CPP_THROW + { + if( (std::uint32_t)(b >> 32) != 0 && a != 0 ) + E::SafeIntOnOverflow(); + + std::uint64_t tmp = b * (std::uint64_t)a; + + if( (std::uint32_t)(tmp >> 32) != 0 ) // overflow + E::SafeIntOnOverflow(); + + *pRet = (std::uint32_t)tmp; + } +}; + +template<> class LargeIntRegMultiply< std::uint32_t, std::int64_t > +{ +public: + _CONSTEXPR14 static bool RegMultiply( std::uint32_t a, const std::int64_t& b, std::uint32_t* pRet ) SAFEINT_NOTHROW + { + if( b < 0 && a != 0 ) + return false; + return LargeIntRegMultiply< std::uint32_t, std::uint64_t >::RegMultiply( a, (std::uint64_t)b, pRet ); + } + + template < typename E > + _CONSTEXPR14 static void RegMultiplyThrow( std::uint32_t a, const std::int64_t& b, std::uint32_t* pRet ) SAFEINT_CPP_THROW + { + if( b < 0 && a != 0 ) + E::SafeIntOnOverflow(); + + LargeIntRegMultiply< std::uint32_t, std::uint64_t >::template RegMultiplyThrow< E >( a, (std::uint64_t)b, pRet ); + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::int64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, const std::int64_t& b, std::int64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + std::int64_t b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( (std::uint64_t)a1, (std::uint64_t)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::int64_t& a, const std::int64_t& b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + std::int64_t b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + LargeIntRegMultiply< std::uint64_t, std::uint64_t >::template RegMultiplyThrow< E >( (std::uint64_t)a1, (std::uint64_t)b1, &tmp ); + + // The unsigned multiplication didn't overflow or we'd be in the exception handler + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::uint32_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, std::uint32_t b, std::int64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ); +#else + bool aNegative = false; + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply( (std::uint64_t)a1, b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::int64_t& a, std::uint32_t b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( (std::uint64_t)a1, b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::int32_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, std::int32_t b, std::int64_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + return IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + std::int64_t b1 = b; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply( (std::uint64_t)a1, (std::uint32_t)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( std::int64_t a, std::int32_t b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + if( !IntrinsicMultiplyInt64( a, (std::int64_t)b, pRet ) ) + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint64_t tmp = 0; + + if( a < 0 ) + { + aNegative = true; + a = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(b); + } + + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( (std::uint64_t)a, (std::uint32_t)b, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int32_t, std::int64_t > +{ +public: + _CONSTEXPR14_MULTIPLY static bool RegMultiply( std::int32_t a, const std::int64_t& b, std::int32_t* pRet ) SAFEINT_NOTHROW + { +#if SAFEINT_USE_INTRINSICS + std::int64_t tmp = 0; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > std::numeric_limits< std::int32_t >::max() || + tmp < std::numeric_limits< std::int32_t >::min() ) + { + return false; + } + + *pRet = (std::int32_t)tmp; + return true; + } + return false; +#else + bool aNegative = false; + bool bNegative = false; + + std::uint32_t tmp = 0; + std::int64_t b1 = b; + + if( a < 0 ) + { + aNegative = true; + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + } + + if( b1 < 0 ) + { + bNegative = true; + b1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b1); + } + + if( LargeIntRegMultiply< std::uint32_t, std::uint64_t >::RegMultiply( (std::uint32_t)a, (std::uint64_t)b1, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::min() ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return true; + } + } + } + + return false; +#endif + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( std::int32_t a, const std::int64_t& b, std::int32_t* pRet ) SAFEINT_CPP_THROW + { +#if SAFEINT_USE_INTRINSICS + std::int64_t tmp; + + if( IntrinsicMultiplyInt64( a, b, &tmp ) ) + { + if( tmp > std::numeric_limits< std::int32_t >::max() || + tmp < std::numeric_limits< std::int32_t >::min() ) + { + E::SafeIntOnOverflow(); + } + + *pRet = (std::int32_t)tmp; + return; + } + E::SafeIntOnOverflow(); +#else + bool aNegative = false; + bool bNegative = false; + + std::uint32_t tmp = 0; + std::int64_t b2 = b; + + if( a < 0 ) + { + aNegative = true; + a = (std::int32_t)AbsValueHelper< std::int32_t, GetAbsMethod< std::int32_t >::method >::Abs(a); + } + + if( b < 0 ) + { + bNegative = true; + b2 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(b2); + } + + LargeIntRegMultiply< std::uint32_t, std::uint64_t >::template RegMultiplyThrow< E >( (std::uint32_t)a, (std::uint64_t)b2, &tmp ); + + // The unsigned multiplication didn't overflow + if( aNegative ^ bNegative ) + { + // Result must be negative + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::min() ) + { + *pRet = SignedNegation< std::int32_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint32_t)std::numeric_limits< std::int32_t >::max() ) + { + *pRet = (std::int32_t)tmp; + return; + } + } + + E::SafeIntOnOverflow(); +#endif + } +}; + +template<> class LargeIntRegMultiply< std::int64_t, std::uint64_t > +{ +public: + // Leave this one as-is - will call unsigned intrinsic internally + _CONSTEXPR14_MULTIPLY static bool RegMultiply( const std::int64_t& a, const std::uint64_t& b, std::int64_t* pRet ) SAFEINT_NOTHROW + { + bool aNegative = false; + + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( (std::uint64_t)a1, (std::uint64_t)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return true; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void RegMultiplyThrow( const std::int64_t& a, const std::uint64_t& b, std::int64_t* pRet ) SAFEINT_CPP_THROW + { + bool aNegative = false; + std::uint64_t tmp = 0; + std::int64_t a1 = a; + + if( a1 < 0 ) + { + aNegative = true; + a1 = (std::int64_t)AbsValueHelper< std::int64_t, GetAbsMethod< std::int64_t >::method >::Abs(a1); + } + + if( LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( (std::uint64_t)a1, (std::uint64_t)b, &tmp ) ) + { + // The unsigned multiplication didn't overflow + if( aNegative ) + { + // Result must be negative + if( tmp <= (std::uint64_t)std::numeric_limits< std::int64_t >::min() ) + { + *pRet = SignedNegation< std::int64_t >::Value( tmp ); + return; + } + } + else + { + // Result must be positive + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + *pRet = (std::int64_t)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +// In all of the following functions where LargeIntRegMultiply methods are called, +// we need to properly transition types. The methods need std::int64_t, std::int32_t, etc. +// but the variables being passed to us could be long long, long int, or long, depending on +// the compiler. Microsoft compiler knows that long long is the same type as std::int64_t, but gcc doesn't + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint64 > +{ +public: + // T, U are std::uint64_t + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isUint64, "T, U must be Uint64" ); + std::uint64_t t1 = t; + std::uint64_t u1 = u; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::uint64_t >::RegMultiply( t1, u1, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow(const std::uint64_t& t, const std::uint64_t& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isUint64, "T, U must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t u1 = u; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::uint64_t >::template RegMultiplyThrow< E >( t1, u1, &tmp ); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Uint > +{ +public: + // T is std::uint64_t + // U is any unsigned int 32-bit or less + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isUint64, "T must be Uint64" ); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::uint32_t >::RegMultiply( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "T must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::uint32_t >::template RegMultiplyThrow< E >( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + } +}; + +// converse of the previous function +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintUint64 > +{ +public: + // T is any unsigned int up to 32-bit + // U is std::uint64_t + _CONSTEXPR14 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::uint32_t tmp = 0; + + if( LargeIntRegMultiply< std::uint32_t, std::uint64_t >::RegMultiply( t, u1, &tmp ) && + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::uint32_t tmp = 0; + + LargeIntRegMultiply< std::uint32_t, std::uint64_t >::template RegMultiplyThrow< E >( t, u1, &tmp ); + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int > +{ +public: + // T is std::uint64_t + // U is any signed int, up to 64-bit + _CONSTEXPR14_MULTIPLY static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "T must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::int32_t >::RegMultiply(t1, (std::int32_t)u, &tmp); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "T must be Uint64"); + std::uint64_t t1 = t; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::int32_t >::template RegMultiplyThrow< E >(t1, (std::int32_t)u, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Uint64Int64 > +{ +public: + // T is std::uint64_t + // U is std::int64_t + _CONSTEXPR14_MULTIPLY static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isInt64, "T must be Uint64, U Int64" ); + std::uint64_t t1 = t; + std::int64_t u1 = u; + std::uint64_t tmp = 0; + bool f = LargeIntRegMultiply< std::uint64_t, std::int64_t >::RegMultiply(t1, u1, &tmp); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64 && safeint_internal::int_traits::isInt64, "T must be Uint64, U Int64"); + std::uint64_t t1 = t; + std::int64_t u1 = u; + std::uint64_t tmp = 0; + LargeIntRegMultiply< std::uint64_t, std::int64_t >::template RegMultiplyThrow< E >(t1, u1, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_UintInt64 > +{ +public: + // T is unsigned up to 32-bit + // U is std::int64_t + _CONSTEXPR14 static bool Multiply(const T& t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isInt64, "U must be Int64"); + std::int64_t u1 = u; + std::uint32_t tmp = 0; + + if( LargeIntRegMultiply< std::uint32_t, std::int64_t >::RegMultiply( (std::uint32_t)t, u1, &tmp ) && + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::Cast(tmp, ret) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(const T& t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "U must be Int64"); + std::int64_t u1 = u; + std::uint32_t tmp = 0; + + LargeIntRegMultiply< std::uint32_t, std::int64_t >::template RegMultiplyThrow< E >( (std::uint32_t)t, u1, &tmp ); + SafeCastHelper< T, std::uint32_t, GetCastMethod< T, std::uint32_t >::method >::template CastThrow< E >(tmp, ret); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint > +{ +public: + // T is std::int64_t + // U is unsigned up to 32-bit + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::uint32_t >::RegMultiply( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::uint32_t >::template RegMultiplyThrow< E >( t1, (std::uint32_t)u, &tmp ); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int64 > +{ +public: + // T, U are std::int64_t + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isInt64, "T, U must be Int64" ); + std::int64_t t1 = t; + std::int64_t u1 = u; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::int64_t >::RegMultiply( t1, u1, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const T& t, const U& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isInt64, "T, U must be Int64"); + std::int64_t t1 = t; + std::int64_t u1 = u; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::int64_t >::template RegMultiplyThrow< E >( t1, u1, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Int > +{ +public: + // T is std::int64_t + // U is signed up to 32-bit + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, U u, T& ret ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::int32_t >::RegMultiply( t1, (std::int32_t)u, &tmp); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const std::int64_t& t, U u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "T must be Int64"); + std::int64_t t1 = t; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::int32_t >::template RegMultiplyThrow< E >(t1, (std::int32_t)u, &tmp); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntUint64 > +{ +public: + // T is signed up to 32-bit + // U is std::uint64_t + _CONSTEXPR14 static bool Multiply(T t, const U& u, T& ret) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::int32_t tmp = 0; + + if( LargeIntRegMultiply< std::int32_t, std::uint64_t >::RegMultiply( (std::int32_t)t, u1, &tmp ) && + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(T t, const std::uint64_t& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + std::uint64_t u1 = u; + std::int32_t tmp = 0; + + LargeIntRegMultiply< std::int32_t, std::uint64_t >::template RegMultiplyThrow< E >( (std::int32_t)t, u1, &tmp ); + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_Int64Uint64> +{ +public: + // T is std::int64_t + // U is std::uint64_t + _CONSTEXPR14_MULTIPLY static bool Multiply( const T& t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isUint64, "T must be Int64, U Uint64" ); + std::int64_t t1 = t; + std::uint64_t u1 = u; + std::int64_t tmp = 0; + bool f = LargeIntRegMultiply< std::int64_t, std::uint64_t >::RegMultiply( t1, u1, &tmp ); + ret = tmp; + return f; + } + + template < typename E > + _CONSTEXPR14_MULTIPLY static void MultiplyThrow( const std::int64_t& t, const std::uint64_t& u, T& ret ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64 && safeint_internal::int_traits::isUint64, "T must be Int64, U Uint64"); + std::int64_t t1 = t; + std::uint64_t u1 = u; + std::int64_t tmp = 0; + LargeIntRegMultiply< std::int64_t, std::uint64_t >::template RegMultiplyThrow< E >( t1, u1, &tmp ); + ret = tmp; + } +}; + +template < typename T, typename U > class MultiplicationHelper< T, U, MultiplicationState_IntInt64> +{ +public: + // T is signed, up to 32-bit + // U is std::int64_t + _CONSTEXPR14 static bool Multiply( T t, const U& u, T& ret ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits::isInt64, "U must be Int64" ); + std::int64_t u1 = u; + std::int32_t tmp = 0; + + if( LargeIntRegMultiply< std::int32_t, std::int64_t >::RegMultiply( (std::int32_t)t, u1, &tmp ) && + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, ret ) ) + { + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void MultiplyThrow(T t, const U& u, T& ret) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isInt64, "U must be Int64"); + std::int64_t u1 = u; + std::int32_t tmp = 0; + + LargeIntRegMultiply< std::int32_t, std::int64_t >::template RegMultiplyThrow< E >( (std::int32_t)t, u1, &tmp ); + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, ret ); + } +}; + +enum DivisionState +{ + DivisionState_OK, + DivisionState_UnsignedSigned, + DivisionState_SignedUnsigned32, + DivisionState_SignedUnsigned64, + DivisionState_SignedUnsigned, + DivisionState_SignedSigned +}; + +template < typename T, typename U > class DivisionMethod +{ +public: + enum + { + method = (safeint_internal::type_compare< T, U >::isBothUnsigned ? DivisionState_OK : + (!std::numeric_limits< T >::is_signed && std::numeric_limits< U >::is_signed) ? DivisionState_UnsignedSigned : + (std::numeric_limits< T >::is_signed && + safeint_internal::int_traits< U >::isUint32 && + safeint_internal::int_traits< T >::isLT64Bit) ? DivisionState_SignedUnsigned32 : + (std::numeric_limits< T >::is_signed && safeint_internal::int_traits< U >::isUint64) ? DivisionState_SignedUnsigned64 : + (std::numeric_limits< T >::is_signed && !std::numeric_limits< U >::is_signed) ? DivisionState_SignedUnsigned : + DivisionState_SignedSigned) + }; +}; + +template < typename T, typename U, int state > class DivisionHelper; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_OK > +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_UnsignedSigned> +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return SafeIntNoError; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return SafeIntNoError; + } + + return SafeIntArithmeticOverflow; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + + if( u == 0 ) + E::SafeIntOnDivZero(); + + if( t == 0 ) + { + result = 0; + return; + } + + if( u > 0 ) + { + result = (T)( t/u ); + return; + } + + // it is always an error to try and divide an unsigned number by a negative signed number + // unless u is bigger than t + if( AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( u ) > t ) + { + result = 0; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned32 > +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + return SafeIntDivideByZero; + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (std::int64_t)t/(std::int64_t)u ); + + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Test for t > 0 + // If t < 0, must explicitly upcast, or implicit upcast to ulong will cause errors + // As it turns out, 32-bit division is about twice as fast, which justifies the extra conditional + + if( t > 0 ) + result = (T)( t/u ); + else + result = (T)( (std::int64_t)t/(std::int64_t)u ); + } +}; + +template < typename T, typename U, bool > class div_signed_uint64; +template < typename T, typename U> class div_signed_uint64 // Value of u fits into an int32 +{ +public: + _CONSTEXPR14 static T divide(T t, U u) { return (T)((std::int32_t)t / (std::int32_t)u); } +}; + +template < typename T, typename U> class div_signed_uint64 +{ +public: + _CONSTEXPR14 static T divide(T t, U u) { return (T)((std::int64_t)t / (std::int64_t)u); } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned64 > +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const std::uint64_t& u, T& result ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + if( u <= (std::uint64_t)std::numeric_limits::max() ) + { + result = div_signed_uint64 < T, U, sizeof(T) < sizeof(std::int64_t) > ::divide(t, u); + } + else // Corner case + if( t == std::numeric_limits::min() && u == (std::uint64_t)std::numeric_limits::min() ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const std::uint64_t& u, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits::isUint64, "U must be Uint64"); + + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + if( u <= (std::uint64_t)std::numeric_limits::max() ) + { + result = div_signed_uint64 < T, U, sizeof(T) < sizeof(std::int64_t) > ::divide(t, u); + } + else // Corner case + if( t == std::numeric_limits::min() && u == (std::uint64_t)std::numeric_limits::min() ) + { + // Min int divided by it's own magnitude is -1 + result = -1; + } + else + { + result = 0; + } + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedUnsigned> +{ +public: + // T is any signed, U is unsigned and smaller than 32-bit + // In this case, standard operator casting is correct + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if( u == 0 ) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + result = (T)( t/u ); + } +}; + +template < typename T, typename U > class DivisionHelper< T, U, DivisionState_SignedSigned> +{ +public: + _CONSTEXPR14 static SafeIntError Divide( const T& t, const U& u, T& result ) SAFEINT_NOTHROW + { + if( u == 0 ) + { + return SafeIntDivideByZero; + } + + if( t == 0 ) + { + result = 0; + return SafeIntNoError; + } + + // Must test for corner case + if( t == std::numeric_limits::min() && u == (U)-1 ) + return SafeIntArithmeticOverflow; + + result = (T)( t/u ); + return SafeIntNoError; + } + + template < typename E > + _CONSTEXPR14 static void DivideThrow( const T& t, const U& u, T& result ) SAFEINT_CPP_THROW + { + if(u == 0) + { + E::SafeIntOnDivZero(); + } + + if( t == 0 ) + { + result = 0; + return; + } + + // Must test for corner case + if( t == std::numeric_limits::min() && u == (U)-1 ) + E::SafeIntOnOverflow(); + + result = (T)( t/u ); + } +}; + +enum AdditionState +{ + AdditionState_CastIntCheckMax, + AdditionState_CastUintCheckOverflow, + AdditionState_CastUintCheckOverflowMax, + AdditionState_CastUint64CheckOverflow, + AdditionState_CastUint64CheckOverflowMax, + AdditionState_CastIntCheckSafeIntMinMax, + AdditionState_CastInt64CheckSafeIntMinMax, + AdditionState_CastInt64CheckMax, + AdditionState_CastUint64CheckSafeIntMinMax, + AdditionState_CastUint64CheckSafeIntMinMax2, + AdditionState_CastInt64CheckOverflow, + AdditionState_CastInt64CheckOverflowSafeIntMinMax, + AdditionState_CastInt64CheckOverflowMax, + AdditionState_ManualCheckInt64Uint64, + AdditionState_ManualCheck, + AdditionState_Error +}; + +template< typename T, typename U > +class AdditionMethod +{ +public: + enum + { + //unsigned-unsigned + method = (IntRegion< T,U >::IntZone_UintLT32_UintLT32 ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Uint32_UintLT64) ? AdditionState_CastUintCheckOverflow : + (IntRegion< T,U >::IntZone_UintLT32_Uint32) ? AdditionState_CastUintCheckOverflowMax : + (IntRegion< T,U >::IntZone_Uint64_Uint) ? AdditionState_CastUint64CheckOverflow : + (IntRegion< T,U >::IntZone_UintLT64_Uint64) ? AdditionState_CastUint64CheckOverflowMax : + //unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? AdditionState_CastUint64CheckSafeIntMinMax2 : + //signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? AdditionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? AdditionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? AdditionState_CastInt64CheckOverflow : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? AdditionState_CastInt64CheckOverflowSafeIntMinMax : + //signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? AdditionState_CastIntCheckMax : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? AdditionState_CastInt64CheckMax : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? AdditionState_CastInt64CheckOverflowMax : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? AdditionState_ManualCheckInt64Uint64 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? AdditionState_ManualCheck : + AdditionState_Error) + }; +}; + +template < typename T, typename U, int method > class AdditionHelper; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //16-bit or less unsigned addition + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //16-bit or less unsigned addition + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflow > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + //we added didn't get smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUintCheckOverflowMax> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //32-bit or less - both are unsigned + std::uint32_t tmp = (std::uint32_t)lhs + (std::uint32_t)rhs; + + // We added and it didn't get smaller or exceed maxInt + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflow> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if(tmp >= lhs) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckOverflowMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //lhs std::uint64_t, rhs unsigned + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it didn't get smaller + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastIntCheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 16-bit or less - one or both are signed + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() && tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 16-bit or less - one or both are signed + std::int32_t tmp = lhs + rhs; + + if( tmp <= (std::int32_t)std::numeric_limits::max() && tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - one or both are signed + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= (std::int64_t)std::numeric_limits::max() && tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - one or both are signed + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= (std::int64_t)std::numeric_limits::max() && tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // 32-bit or less - lhs signed, rhs unsigned + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // 32-bit or less - lhs signed, rhs unsigned + std::int64_t tmp = (std::int64_t)lhs + (std::int64_t)rhs; + + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is std::uint64_t, rhs signed + std::uint64_t tmp = 0; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return true; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is std::uint64_t, rhs signed + std::uint64_t tmp = 0; + + if( rhs < 0 ) + { + // So we're effectively subtracting + tmp = AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if( tmp <= lhs ) + { + result = lhs - tmp; + return; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // We added and it did not become smaller + if( tmp >= lhs ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastUint64CheckSafeIntMinMax2> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is unsigned and < 64-bit, rhs std::int64_t + if( rhs < 0 ) + { + if( lhs >= ~(std::uint64_t)( rhs ) + 1 )//negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return true; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is unsigned and < 64-bit, rhs std::int64_t + if( rhs < 0 ) + { + if( lhs >= ~(std::uint64_t)( rhs ) + 1) //negation is safe, since rhs is 64-bit + { + result = (T)( lhs + rhs ); + return; + } + } + else + { + // now we know that rhs can be safely cast into an std::uint64_t + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + // special case - rhs cannot be larger than 0x7fffffffffffffff, lhs cannot be larger than 0xffffffff + // it is not possible for the operation above to overflow, so just check max + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflow> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is std::int64_t, rhs signed + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs + (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + return false; + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is std::int64_t, rhs signed + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs + (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // mixed sign cannot overflow + if( rhs >= 0 && tmp < lhs ) + E::SafeIntOnOverflow(); + } + else + { + // lhs negative + if( rhs < 0 && tmp > lhs ) + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowSafeIntMinMax> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //rhs is std::int64_t, lhs signed + std::int64_t tmp = 0; + + if( AdditionHelper< std::int64_t, std::int64_t, AdditionState_CastInt64CheckOverflow >::Addition( (std::int64_t)lhs, (std::int64_t)rhs, tmp ) && + tmp <= std::numeric_limits::max() && + tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + //rhs is std::int64_t, lhs signed + std::int64_t tmp = 0; + + AdditionHelper< std::int64_t, std::int64_t, AdditionState_CastInt64CheckOverflow >::AdditionThrow< E >( (std::int64_t)lhs, (std::int64_t)rhs, tmp ); + + if( tmp <= std::numeric_limits::max() && + tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_CastInt64CheckOverflowMax> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + //lhs is std::int64_t, rhs unsigned < 64-bit + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (T)(std::int64_t)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is std::int64_t, rhs unsigned < 64-bit + // Some compilers get optimization-happy, let's thwart them + + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (T)(std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheckInt64Uint64 > +{ +public: + _CONSTEXPR14 static bool Addition( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64" ); + // rhs is std::uint64_t, lhs std::int64_t + // cast everything to unsigned, perform addition, then + // cast back for check - this is done to stop optimizers from removing the code + std::uint64_t tmp = (std::uint64_t)lhs + rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (std::int64_t)tmp; + return true; + } + + result = 0; + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64"); + // rhs is std::uint64_t, lhs std::int64_t + std::uint64_t tmp = (std::uint64_t)lhs + rhs; + + if( (std::int64_t)tmp >= lhs ) + { + result = (std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class AdditionHelper < T, U, AdditionState_ManualCheck> +{ +public: + _CONSTEXPR14 static bool Addition( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // rhs is std::uint64_t, lhs signed, 32-bit or less + if( (std::uint32_t)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + // Note - this is tweaked to keep optimizers from tossing out the code. + std::uint32_t tmp = (std::uint32_t)rhs + (std::uint32_t)lhs; + + if( (std::int32_t)tmp >= lhs && SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( (std::int32_t)tmp, result ) ) + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void AdditionThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // rhs is std::uint64_t, lhs signed, 32-bit or less + + if( (std::uint32_t)( rhs >> 32 ) == 0 ) + { + // Now it just happens to work out that the standard behavior does what we want + // Adding explicit casts to show exactly what's happening here + std::uint32_t tmp = (std::uint32_t)rhs + (std::uint32_t)lhs; + + if( (std::int32_t)tmp >= lhs ) + { + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( (std::int32_t)tmp, result ); + return; + } + } + E::SafeIntOnOverflow(); + } +}; + +enum SubtractionState +{ + SubtractionState_BothUnsigned, + SubtractionState_CastIntCheckSafeIntMinMax, + SubtractionState_CastIntCheckMin, + SubtractionState_CastInt64CheckSafeIntMinMax, + SubtractionState_CastInt64CheckMin, + SubtractionState_Uint64Int, + SubtractionState_UintInt64, + SubtractionState_Int64Int, + SubtractionState_IntInt64, + SubtractionState_Int64Uint, + SubtractionState_IntUint64, + SubtractionState_Int64Uint64, + // states for SubtractionMethod2 + SubtractionState_BothUnsigned2, + SubtractionState_CastIntCheckSafeIntMinMax2, + SubtractionState_CastInt64CheckSafeIntMinMax2, + SubtractionState_Uint64Int2, + SubtractionState_UintInt642, + SubtractionState_Int64Int2, + SubtractionState_IntInt642, + SubtractionState_Int64Uint2, + SubtractionState_IntUint642, + SubtractionState_Int64Uint642, + SubtractionState_Error +}; + +template < typename T, typename U > class SubtractionMethod +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt64 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt64 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckMin : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckMin : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint64 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint64 : + SubtractionState_Error) + }; +}; + +// this is for the case of U - SafeInt< T, E > +template < typename T, typename U > class SubtractionMethod2 +{ +public: + enum + { + // unsigned-unsigned + method = ((IntRegion< T,U >::IntZone_UintLT32_UintLT32 || + (IntRegion< T,U >::IntZone_Uint32_UintLT64) || + (IntRegion< T,U >::IntZone_UintLT32_Uint32) || + (IntRegion< T,U >::IntZone_Uint64_Uint) || + (IntRegion< T,U >::IntZone_UintLT64_Uint64)) ? SubtractionState_BothUnsigned2 : + // unsigned-signed + (IntRegion< T,U >::IntZone_UintLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint32_IntLT64 || + IntRegion< T,U >::IntZone_UintLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Uint64_Int || + IntRegion< T,U >::IntZone_Uint64_Int64) ? SubtractionState_Uint64Int2 : + (IntRegion< T,U >::IntZone_UintLT64_Int64) ? SubtractionState_UintInt642 : + // signed-signed + (IntRegion< T,U >::IntZone_IntLT32_IntLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_IntLT64 || + IntRegion< T,U >::IntZone_IntLT32_Int32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_Int || + IntRegion< T,U >::IntZone_Int64_Int64) ? SubtractionState_Int64Int2 : + (IntRegion< T,U >::IntZone_IntLT64_Int64) ? SubtractionState_IntInt642 : + // signed-unsigned + (IntRegion< T,U >::IntZone_IntLT32_UintLT32) ? SubtractionState_CastIntCheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int32_UintLT32 || + IntRegion< T,U >::IntZone_IntLT64_Uint32) ? SubtractionState_CastInt64CheckSafeIntMinMax2 : + (IntRegion< T,U >::IntZone_Int64_UintLT64) ? SubtractionState_Int64Uint2 : + (IntRegion< T,U >::IntZone_Int_Uint64) ? SubtractionState_IntUint642 : + (IntRegion< T,U >::IntZone_Int64_Uint64) ? SubtractionState_Int64Uint642 : + SubtractionState_Error) + }; +}; + +template < typename T, typename U, int method > class SubtractionHelper; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + result = (T)( lhs - rhs ); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_BothUnsigned2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, U& result ) SAFEINT_NOTHROW + { + // both are unsigned - easy case + // Except we do have to check for overflow - lhs could be larger than result can hold + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + return SafeCastHelper< U, T, GetCastMethod::method>::Cast( tmp, result); + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, U& result ) SAFEINT_CPP_THROW + { + // both are unsigned - easy case + if( rhs <= lhs ) + { + T tmp = (T)(lhs - rhs); + SafeCastHelper< U, T, GetCastMethod::method >::template CastThrow( tmp, result); + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + if( SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, result ) ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastIntCheckSafeIntMinMax2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + return SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::Cast( tmp, result ); + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int32_t tmp = lhs - rhs; + + SafeCastHelper< T, std::int32_t, GetCastMethod< T, std::int32_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastIntCheckMin > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + std::int32_t tmp = lhs - rhs; + + if( tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 16-bit or less + // rhs is unsigned - check only minimum + std::int32_t tmp = lhs - rhs; + + if( tmp >= (std::int32_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckSafeIntMinMax > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + return SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::Cast( tmp, result ); + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_CastInt64CheckSafeIntMinMax2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + return SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::Cast( tmp, result ); + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is signed, so could end up increasing or decreasing + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + SafeCastHelper< T, std::int64_t, GetCastMethod< T, std::int64_t >::method >::template CastThrow< E >( tmp, result ); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_CastInt64CheckMin > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + if( tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // both values are 32-bit or less + // rhs is unsigned - check only minimum + std::int64_t tmp = (std::int64_t)lhs - (std::int64_t)rhs; + + if( tmp >= (std::int64_t)std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Uint64Int > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an std::uint64_t, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (std::uint64_t)rhs ); + return true; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an std::uint64_t, rhs signed + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (std::uint64_t)rhs ); + return; + } + } + else + { + T tmp = lhs; + // we're now effectively adding + result = lhs + AbsValueHelper< U, GetAbsMethod< U >::method >::Abs( rhs ); + + if(result >= tmp) + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Uint64Int2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U is std::uint64_t, T is signed + if( rhs < 0 ) + { + // treat this as addition + std::uint64_t tmp = 0; + + tmp = lhs + (std::uint64_t)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + else if( (std::uint64_t)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return true; + } + else + { + // result is positive + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U is std::uint64_t, T is signed + if( rhs < 0 ) + { + // treat this as addition + std::uint64_t tmp = 0; + + tmp = lhs + (std::uint64_t)AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( rhs ); + + // must check for addition overflow and max + if( tmp >= lhs && tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + else if( (std::uint64_t)rhs > lhs ) // now both are positive, so comparison always works + { + // result is negative + // implies that lhs must fit into T, and result cannot overflow + // Also allows us to drop to 32-bit math, which is faster on a 32-bit system + result = (T)lhs - (T)rhs; + return; + } + else + { + // result is positive + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_UintInt64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an unsigned int32 or smaller, rhs std::int64_t + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return true; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + std::uint64_t tmp = lhs + ~(std::uint64_t)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= std::numeric_limits::max()) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an unsigned int32 or smaller, rhs std::int64_t + // must first see if rhs is positive or negative + if( rhs >= 0 ) + { + if( (std::uint64_t)rhs <= lhs ) + { + result = (T)( lhs - (T)rhs ); + return; + } + } + else + { + // we're now effectively adding + // since lhs is 32-bit, and rhs cannot exceed 2^63 + // this addition cannot overflow + std::uint64_t tmp = lhs + ~(std::uint64_t)( rhs ) + 1; // negation safe + + // but we could exceed MaxInt + if(tmp <= std::numeric_limits::max()) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template class SubtractionHelper< U, T, SubtractionState_UintInt642 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // U unsigned 32-bit or less, T std::int64_t + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (std::int64_t)lhs - rhs ); + return true; + } + else + { + // we effectively have an addition + // which cannot overflow internally + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)( -rhs ); + + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // U unsigned 32-bit or less, T std::int64_t + if( rhs >= 0 ) + { + // overflow not possible + result = (T)( (std::int64_t)lhs - rhs ); + return; + } + else + { + // we effectively have an addition + // which cannot overflow internally + std::uint64_t tmp = (std::uint64_t)lhs + (std::uint64_t)( -rhs ); + + if( tmp <= (std::uint64_t)std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Int > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is an std::int64_t, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + return false; + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is an std::int64_t, rhs signed (up to 64-bit) + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + // Note - ideally, we can order these so that true conditionals + // lead to success, which enables better pipelining + // It isn't practical here + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || // condition 2 + ( rhs >= 0 && tmp > lhs ) ) // condition 3 + { + E::SafeIntOnOverflow(); + } + + result = (T)tmp; + } +}; + +template < typename T, typename U, bool > class subtract_corner_case_max; + +template < typename T, typename U> class subtract_corner_case_max < T, U, true> +{ +public: + _CONSTEXPR14 static bool isOverflowPositive(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (tmp > std::numeric_limits::max() || (rhs < 0 && tmp < lhs)); + } + + _CONSTEXPR14 static bool isOverflowNegative(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (tmp < std::numeric_limits::min() || (rhs >= 0 && tmp > lhs)); + } +}; + +template < typename T, typename U> class subtract_corner_case_max < T, U, false> +{ +public: + _CONSTEXPR14 static bool isOverflowPositive(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (rhs < 0 && tmp < lhs); + } + + _CONSTEXPR14 static bool isOverflowNegative(const T& rhs, const U& lhs, std::int64_t tmp) + { + return (rhs >= 0 && tmp > lhs); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Int2 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs std::int64_t, rhs any signed int (including std::int64_t) + std::int64_t tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if(subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit >::isOverflowPositive(rhs, lhs, tmp)) + { + return false; + } + } + else + { + // lhs negative + if(subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit >::isOverflowNegative(rhs, lhs, tmp)) + { + return false; + } + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs std::int64_t, rhs any signed int (including std::int64_t) + std::int64_t tmp = lhs - rhs; + + // we have essentially 4 cases: + // + // 1) lhs positive, rhs positive - overflow not possible in tmp + // 2) lhs positive, rhs negative - equivalent to addition - result >= lhs or error + // 3) lhs negative, rhs positive - check result <= lhs + // 4) lhs negative, rhs negative - overflow not possible in tmp + + if( lhs >= 0 ) + { + // if both positive, overflow to negative not possible + // which is why we'll explicitly check maxInt, and not call SafeCast + if (subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit>::isOverflowPositive(rhs, lhs, tmp)) + { + E::SafeIntOnOverflow(); + } + } + else + { + // lhs negative + if (subtract_corner_case_max< T, U, safeint_internal::int_traits< T >::isLT64Bit >::isOverflowNegative(rhs, lhs, tmp)) + { + E::SafeIntOnOverflow(); + } + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntInt64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 32-bit int or less, rhs std::int64_t + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return true; + } + } + else + { + // fourth case + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return true; + } + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 32-bit int or less, rhs std::int64_t + // we have essentially 4 cases: + // + // lhs positive, rhs positive - rhs could be larger than lhs can represent + // lhs positive, rhs negative - additive case - check tmp >= lhs and tmp > max int + // lhs negative, rhs positive - check tmp <= lhs and tmp < min int + // lhs negative, rhs negative - addition cannot internally overflow, check against max + + std::int64_t tmp = (std::int64_t)((std::uint64_t)lhs - (std::uint64_t)rhs); + + if( lhs >= 0 ) + { + // first case + if( rhs >= 0 ) + { + if( tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + } + else + { + // second case + if( tmp >= lhs && tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + } + else + { + // lhs < 0 + // third case + if( rhs >= 0 ) + { + if( tmp <= lhs && tmp >= std::numeric_limits::min() ) + { + result = (T)tmp; + return; + } + } + else + { + // fourth case + if( tmp <= std::numeric_limits::max() ) + { + result = (T)tmp; + return; + } + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntInt642 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int32 or smaller, rhs is int64 + std::int64_t tmp = (std::int64_t)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + return false; + //else OK + } + + result = (T)tmp; + return true; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int32 or smaller, rhs is int64 + std::int64_t tmp = (std::int64_t)lhs - rhs; + + if( ( lhs >= 0 && rhs < 0 && tmp < lhs ) || + ( rhs > 0 && tmp > lhs ) ) + { + E::SafeIntOnOverflow(); + //else OK + } + + result = (T)tmp; + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (T)(std::int64_t)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is a 64-bit int, rhs unsigned int32 or smaller + // perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (T)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint2 > +{ +public: + // lhs is std::int64_t, rhs is unsigned 32-bit or smaller + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // Do this as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= std::numeric_limits::max() && (std::int64_t)tmp >= std::numeric_limits::min() ) + { + result = (T)(std::int64_t)tmp; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // Do this as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - (std::uint64_t)rhs; + + if( (std::int64_t)tmp <= std::numeric_limits::max() && (std::int64_t)tmp >= std::numeric_limits::min() ) + { + result = (T)(std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_IntUint64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const T& lhs, const U& rhs, T& result ) SAFEINT_NOTHROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of std::numeric_limits::min() + // This will give it to us without extraneous compiler warnings + const std::uint64_t AbsMinIntT = (std::uint64_t)std::numeric_limits::max() + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return true; + } + } + else + { + if( rhs <= AbsMinIntT + (std::uint64_t)lhs ) + { + result = (T)( lhs - rhs ); + return true; + } + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const T& lhs, const U& rhs, T& result ) SAFEINT_CPP_THROW + { + // lhs is any signed int, rhs unsigned int64 + // check against available range + + // We need the absolute value of std::numeric_limits::min() + // This will give it to us without extraneous compiler warnings + const std::uint64_t AbsMinIntT = (std::uint64_t)std::numeric_limits::max() + 1; + + if( lhs < 0 ) + { + if( rhs <= AbsMinIntT - AbsValueHelper< T, GetAbsMethod< T >::method >::Abs( lhs ) ) + { + result = (T)( lhs - rhs ); + return; + } + } + else + { + if( rhs <= AbsMinIntT + (std::uint64_t)lhs ) + { + result = (T)( lhs - rhs ); + return; + } + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_IntUint642 > +{ +public: + _CONSTEXPR14 static bool Subtract( const U& lhs, const T& rhs, T& result ) SAFEINT_NOTHROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const U& lhs, const T& rhs, T& result ) SAFEINT_CPP_THROW + { + // We run into upcasting problems on comparison - needs 2 checks + if( lhs >= 0 && (T)lhs >= rhs ) + { + result = (T)((U)lhs - (U)rhs); + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename T, typename U > class SubtractionHelper< T, U, SubtractionState_Int64Uint64 > +{ +public: + _CONSTEXPR14 static bool Subtract( const std::int64_t& lhs, const std::uint64_t& rhs, std::int64_t& result ) SAFEINT_NOTHROW + { + static_assert(safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64"); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (std::int64_t)tmp; + return true; + } + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits< T >::isInt64 && safeint_internal::int_traits< U >::isUint64, "T must be Int64, U Uint64"); + // if we subtract, and it gets larger, there's a problem + // Perform test as unsigned to prevent unwanted optimizations + std::uint64_t tmp = (std::uint64_t)lhs - rhs; + + if( (std::int64_t)tmp <= lhs ) + { + result = (std::int64_t)tmp; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +template < typename U, typename T > class SubtractionHelper< U, T, SubtractionState_Int64Uint642 > +{ +public: + // If lhs is negative, immediate problem - return must be positive, and subtracting only makes it + // get smaller. If rhs > lhs, then it would also go negative, which is the other case + _CONSTEXPR14 static bool Subtract( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_NOTHROW + { + static_assert( safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64, "T must be Uint64, U Int64" ); + if( lhs >= 0 && (std::uint64_t)lhs >= rhs ) + { + result = (std::uint64_t)lhs - rhs; + return true; + } + + return false; + } + + template < typename E > + _CONSTEXPR14 static void SubtractThrow( const std::int64_t& lhs, const std::uint64_t& rhs, T& result ) SAFEINT_CPP_THROW + { + static_assert(safeint_internal::int_traits< T >::isUint64 && safeint_internal::int_traits< U >::isInt64, "T must be Uint64, U Int64"); + if( lhs >= 0 && (std::uint64_t)lhs >= rhs ) + { + result = (std::uint64_t)lhs - rhs; + return; + } + + E::SafeIntOnOverflow(); + } + +}; + +enum BinaryState +{ + BinaryState_OK, + BinaryState_Int8, + BinaryState_Int16, + BinaryState_Int32 +}; + +template < typename T, typename U > class BinaryMethod +{ +public: + enum + { + // If both operands are unsigned OR + // return type is smaller than rhs OR + // return type is larger and rhs is unsigned + // Then binary operations won't produce unexpected results + method = ( sizeof( T ) <= sizeof( U ) || + safeint_internal::type_compare< T, U >::isBothUnsigned || + !std::numeric_limits< U >::is_signed ) ? BinaryState_OK : + safeint_internal::int_traits< U >::isInt8 ? BinaryState_Int8 : + safeint_internal::int_traits< U >::isInt16 ? BinaryState_Int16 + : BinaryState_Int32 + }; +}; + +#ifdef SAFEINT_DISABLE_BINARY_ASSERT +#define BinaryAssert(x) +#else +#define BinaryAssert(x) SAFEINT_ASSERT(x) +#endif + +template < typename T, typename U, int method > class BinaryAndHelper; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_OK > +{ +public: + _CONSTEXPR11 static T And( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs & rhs ); } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int8 > +{ +public: + _CONSTEXPR14 static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (std::uint8_t)rhs ) ); + return (T)( lhs & (std::uint8_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int16 > +{ +public: + _CONSTEXPR14 static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (std::uint16_t)rhs ) ); + return (T)( lhs & (std::uint16_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryAndHelper< T, U, BinaryState_Int32 > +{ +public: + _CONSTEXPR14 static T And( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs & rhs ) == ( lhs & (std::uint32_t)rhs ) ); + return (T)( lhs & (std::uint32_t)rhs ); + } +}; + +template < typename T, typename U, int method > class BinaryOrHelper; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_OK > +{ +public: + _CONSTEXPR11 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs | rhs ); } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int8 > +{ +public: + _CONSTEXPR14 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (std::uint8_t)rhs ) ); + return (T)( lhs | (std::uint8_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int16 > +{ +public: + _CONSTEXPR14 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (std::uint16_t)rhs ) ); + return (T)( lhs | (std::uint16_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryOrHelper< T, U, BinaryState_Int32 > +{ +public: + _CONSTEXPR14 static T Or( T lhs, U rhs ) SAFEINT_NOTHROW + { + //cast forces sign extension to be zeros + BinaryAssert( ( lhs | rhs ) == ( lhs | (std::uint32_t)rhs ) ); + return (T)( lhs | (std::uint32_t)rhs ); + } +}; + +template class BinaryXorHelper; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_OK > +{ +public: + _CONSTEXPR11 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW { return (T)( lhs ^ rhs ); } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int8 > +{ +public: + _CONSTEXPR14 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (std::uint8_t)rhs ) ); + return (T)( lhs ^ (std::uint8_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int16 > +{ +public: + _CONSTEXPR14 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (std::uint16_t)rhs ) ); + return (T)( lhs ^ (std::uint16_t)rhs ); + } +}; + +template < typename T, typename U > class BinaryXorHelper< T, U, BinaryState_Int32 > +{ +public: + _CONSTEXPR14 static T Xor( T lhs, U rhs ) SAFEINT_NOTHROW + { + // cast forces sign extension to be zeros + BinaryAssert( ( lhs ^ rhs ) == ( lhs ^ (std::uint32_t)rhs ) ); + return (T)( lhs ^ (std::uint32_t)rhs ); + } +}; + +/***************** External functions ****************************************/ + +// External functions that can be used where you only need to check one operation +// non-class helper function so that you can check for a cast's validity +// and handle errors how you like +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeCast( const T From, U& To ) SAFEINT_NOTHROW +{ + return SafeCastHelper< U, T, GetCastMethod< U, T >::method >::Cast( From, To ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeNotEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeGreaterThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeGreaterThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeLessThan( const T t, const U u ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( u, t ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeLessThanEquals( const T t, const U u ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( t, u ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeModulus( const T& t, const U& u, T& result ) SAFEINT_NOTHROW +{ + return ( ModulusHelper< T, U, ValidComparison< T, U >::method >::Modulus( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +_CONSTEXPR14_MULTIPLY inline bool SafeMultiply( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::Multiply( t, u, result ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeDivide( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return ( DivisionHelper< T, U, DivisionMethod< T, U >::method >::Divide( t, u, result ) == SafeIntNoError ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeAdd( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return AdditionHelper< T, U, AdditionMethod< T, U >::method >::Addition( t, u, result ); +} + +template < typename T, typename U > +_CONSTEXPR11 inline bool SafeSubtract( T t, U u, T& result ) SAFEINT_NOTHROW +{ + return SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::Subtract( t, u, result ); +} + +/***************** end external functions ************************************/ + +// Main SafeInt class +// Assumes exceptions can be thrown +template < typename T, typename E = SafeIntDefaultExceptionHandler > class SafeInt +{ +public: + _CONSTEXPR11 SafeInt() SAFEINT_NOTHROW : m_int(0) + { + static_assert( safeint_internal::numeric_type< T >::isInt, "Integer type required" ); + } + + // Having a constructor for every type of int + // avoids having the compiler evade our checks when doing implicit casts - + // e.g., SafeInt s = 0x7fffffff; + _CONSTEXPR11 SafeInt( const T& i ) SAFEINT_NOTHROW : m_int(i) + { + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + //always safe + } + + // provide explicit boolean converter + _CONSTEXPR11 SafeInt( bool b ) SAFEINT_NOTHROW : m_int((T)(b ? 1 : 0)) + { + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + } + + template < typename U > + _CONSTEXPR14 SafeInt(const SafeInt< U, E >& u) SAFEINT_CPP_THROW : m_int(0) + { + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + m_int = (T)SafeInt< T, E >( (U)u ); + } + + template < typename U > + _CONSTEXPR14 SafeInt( const U& i ) SAFEINT_CPP_THROW : m_int(0) + { + // m_int must be initialized to something to work with constexpr, because if it throws, then m_int is unknown + static_assert(safeint_internal::numeric_type< T >::isInt, "Integer type required"); + // SafeCast will throw exceptions if i won't fit in type T + + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( i, m_int ); + } + + // The destructor is intentionally commented out - no destructor + // vs. a do-nothing destructor makes a huge difference in + // inlining characteristics. It wasn't doing anything anyway. + // ~SafeInt(){}; + + + // now start overloading operators + // assignment operator + // constructors exist for all int types and will ensure safety + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator =( const U& rhs ) SAFEINT_CPP_THROW + { + // use constructor to test size + // constructor is optimized to do minimal checking based + // on whether T can contain U + // note - do not change this + m_int = SafeInt< T, E >( rhs ); + return *this; + } + + _CONSTEXPR14 SafeInt< T, E >& operator =( const T& rhs ) SAFEINT_NOTHROW + { + m_int = rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator =( const SafeInt< U, E >& rhs ) SAFEINT_CPP_THROW + { + SafeCastHelper< T, U, GetCastMethod< T, U >::method >::template CastThrow< E >( rhs.Ref(), m_int ); + return *this; + } + + _CONSTEXPR14 SafeInt< T, E >& operator =( const SafeInt< T, E >& rhs ) SAFEINT_NOTHROW + { + m_int = rhs.m_int; + return *this; + } + + // Casting operators + + _CONSTEXPR11 operator bool() const SAFEINT_NOTHROW + { + return !!m_int; + } + + _CONSTEXPR14 operator char() const SAFEINT_CPP_THROW + { + char val = 0; + SafeCastHelper< char, T, GetCastMethod< char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator signed char() const SAFEINT_CPP_THROW + { + signed char val = 0; + SafeCastHelper< signed char, T, GetCastMethod< signed char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned char() const SAFEINT_CPP_THROW + { + unsigned char val = 0; + SafeCastHelper< unsigned char, T, GetCastMethod< unsigned char, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator short() const SAFEINT_CPP_THROW + { + short val = 0; + SafeCastHelper< short, T, GetCastMethod< short, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned short() const SAFEINT_CPP_THROW + { + unsigned short val = 0; + SafeCastHelper< unsigned short, T, GetCastMethod< unsigned short, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator int() const SAFEINT_CPP_THROW + { + int val = 0; + SafeCastHelper< int, T, GetCastMethod< int, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned int() const SAFEINT_CPP_THROW + { + unsigned int val = 0; + SafeCastHelper< unsigned int, T, GetCastMethod< unsigned int, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // The compiler knows that int == std::int32_t + // but not that long == std::int32_t, because on some systems, long == std::int64_t + _CONSTEXPR14 operator long() const SAFEINT_CPP_THROW + { + long val = 0; + SafeCastHelper< long, T, GetCastMethod< long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned long() const SAFEINT_CPP_THROW + { + unsigned long val = 0; + SafeCastHelper< unsigned long, T, GetCastMethod< unsigned long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator long long() const SAFEINT_CPP_THROW + { + long long val = 0; + SafeCastHelper< long long, T, GetCastMethod< long long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator unsigned long long() const SAFEINT_CPP_THROW + { + unsigned long long val = 0; + SafeCastHelper< unsigned long long, T, GetCastMethod< unsigned long long, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator wchar_t() const SAFEINT_CPP_THROW + { + wchar_t val = 0; + SafeCastHelper< wchar_t, T, GetCastMethod< wchar_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + +#ifdef SIZE_T_CAST_NEEDED + // We also need an explicit cast to size_t, or the compiler will complain + // Apparently, only SOME compilers complain, and cl 14.00.50727.42 isn't one of them + // Leave here in case we decide to backport this to an earlier compiler + _CONSTEXPR14 operator size_t() const SAFEINT_CPP_THROW + { + size_t val = 0; + SafeCastHelper< size_t, T, GetCastMethod< size_t, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } +#endif + + // Also provide a cast operator for floating point types + _CONSTEXPR14 operator float() const SAFEINT_CPP_THROW + { + float val = 0.0; + SafeCastHelper< float, T, GetCastMethod< float, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + _CONSTEXPR14 operator double() const SAFEINT_CPP_THROW + { + double val = 0.0; + SafeCastHelper< double, T, GetCastMethod< double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + _CONSTEXPR14 operator long double() const SAFEINT_CPP_THROW + { + long double val = 0.0; + SafeCastHelper< long double, T, GetCastMethod< long double, T >::method >::template CastThrow< E >( m_int, val ); + return val; + } + + // If you need a pointer to the data + // this could be dangerous, but allows you to correctly pass + // instances of this class to APIs that take a pointer to an integer + // also see overloaded address-of operator below + T* Ptr() SAFEINT_NOTHROW { return &m_int; } + const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } + _CONSTEXPR14 const T& Ref() const SAFEINT_NOTHROW { return m_int; } + + // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload + // operator & + // This allows you to do unsafe things! + // It is meant to allow you to more easily + // pass a SafeInt into things like ReadFile + T* operator &() SAFEINT_NOTHROW { return &m_int; } + const T* operator &() const SAFEINT_NOTHROW { return &m_int; } + + // Unary operators + _CONSTEXPR11 bool operator !() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } + + // operator + (unary) + // note - normally, the '+' and '-' operators will upcast to a signed int + // for T < 32 bits. This class changes behavior to preserve type + _CONSTEXPR11 const SafeInt< T, E >& operator +() const SAFEINT_NOTHROW { return *this; } + + //unary - + + _CONSTEXPR14 SafeInt< T, E > operator -() const SAFEINT_CPP_THROW + { + // Note - unsigned still performs the bitwise manipulation + // will warn at level 2 or higher if the value is 32-bit or larger + return SafeInt(NegationHelper::is_signed>::template NegativeThrow(m_int)); + } + + // prefix increment operator + _CONSTEXPR14 SafeInt< T, E >& operator ++() SAFEINT_CPP_THROW + { + if( m_int != std::numeric_limits::max() ) + { + ++m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // prefix decrement operator + _CONSTEXPR14 SafeInt< T, E >& operator --() SAFEINT_CPP_THROW + { + if( m_int != std::numeric_limits::min() ) + { + --m_int; + return *this; + } + E::SafeIntOnOverflow(); + } + + // note that postfix operators have inherently worse perf + // characteristics + + // postfix increment operator + _CONSTEXPR14 SafeInt< T, E > operator ++( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != std::numeric_limits::max() ) + { + SafeInt< T, E > tmp( m_int ); + + m_int++; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // postfix decrement operator + _CONSTEXPR14 SafeInt< T, E > operator --( int ) SAFEINT_CPP_THROW // dummy arg to comply with spec + { + if( m_int != std::numeric_limits::min() ) + { + SafeInt< T, E > tmp( m_int ); + m_int--; + return tmp; + } + E::SafeIntOnOverflow(); + } + + // One's complement + // Note - this operator will normally change size to an int + // cast in return improves perf and maintains type + _CONSTEXPR11 SafeInt< T, E > operator ~() const SAFEINT_NOTHROW { return SafeInt< T, E >( (T)~m_int ); } + + // Binary operators + // + // arithmetic binary operators + // % modulus + // * multiplication + // / division + // + addition + // - subtraction + // + // For each of the arithmetic operators, you will need to + // use them as follows: + // + // SafeInt c = 2; + // SafeInt i = 3; + // + // SafeInt i2 = i op (char)c; + // OR + // SafeInt i2 = (int)i op c; + // + // The base problem is that if the lhs and rhs inputs are different SafeInt types + // it is not possible in this implementation to determine what type of SafeInt + // should be returned. You have to let the class know which of the two inputs + // need to be the return type by forcing the other value to the base integer type. + // + // Note - as per feedback from Scott Meyers, I'm exploring how to get around this. + // 3.0 update - I'm still thinking about this. It can be done with template metaprogramming, + // but it is tricky, and there's a perf vs. correctness tradeoff where the right answer + // is situational. + // + // The case of: + // + // SafeInt< T, E > i, j, k; + // i = j op k; + // + // works just fine and no unboxing is needed because the return type is not ambiguous. + + // Modulus + // Modulus has some convenient properties - + // first, the magnitude of the return can never be + // larger than the lhs operand, and it must be the same sign + // as well. It does, however, suffer from the same promotion + // problems as comparisons, division and other operations + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator %( U rhs ) const SAFEINT_CPP_THROW + { + T result = 0; + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + _CONSTEXPR14 SafeInt< T, E > operator %( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T result = 0; + ModulusHelper< T, T, ValidComparison< T, T >::method >::template ModulusThrow< E >( m_int, rhs, result ); + return SafeInt< T, E >( result ); + } + + // Modulus assignment + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator %=( U rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator %=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Multiplication + template < typename U > + _CONSTEXPR14_MULTIPLY SafeInt< T, E > operator *( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + _CONSTEXPR14 SafeInt< T, E > operator *( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Multiplication assignment + _CONSTEXPR14 SafeInt< T, E >& operator *=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, T, MultiplicationMethod< T, T >::method >::template MultiplyThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14_MULTIPLY SafeInt< T, E >& operator *=( U rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14_MULTIPLY SafeInt< T, E >& operator *=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( m_int, rhs.Ref(), m_int ); + return *this; + } + + // Division + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator /( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + _CONSTEXPR14 SafeInt< T, E > operator /( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Division assignment + _CONSTEXPR14 SafeInt< T, E >& operator /=( SafeInt< T, E > i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, T, DivisionMethod< T, T >::method >::template DivideThrow< E >( m_int, (T)i, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator /=( U i ) SAFEINT_CPP_THROW + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, i, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator /=( SafeInt< U, E > i ) + { + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( m_int, (U)i, m_int ); + return *this; + } + + // For addition and subtraction + + // Addition + _CONSTEXPR14 SafeInt< T, E > operator +( SafeInt< T, E > rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator +( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + //addition assignment + _CONSTEXPR14 SafeInt< T, E >& operator +=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, T, AdditionMethod< T, T >::method >::template AdditionThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator +=( U rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator +=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Subtraction + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator -( U rhs ) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, ret ); + return SafeInt< T, E >( ret ); + } + + _CONSTEXPR14 SafeInt< T, E > operator -(SafeInt< T, E > rhs) const SAFEINT_CPP_THROW + { + T ret( 0 ); + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, ret ); + return SafeInt< T, E >( ret ); + } + + // Subtraction assignment + _CONSTEXPR14 SafeInt< T, E >& operator -=( SafeInt< T, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, T, SubtractionMethod< T, T >::method >::template SubtractThrow< E >( m_int, (T)rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator -=( U rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, rhs, m_int ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator -=( SafeInt< U, E > rhs ) SAFEINT_CPP_THROW + { + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( m_int, (U)rhs, m_int ); + return *this; + } + + // Shift operators + // Note - shift operators ALWAYS return the same type as the lhs + // specific version for SafeInt< T, E > not needed - + // code path is exactly the same as for SafeInt< U, E > as rhs + + // Left shift + // Also, shifting > bitcount is undefined - trap in debug + #define ShiftAssert(x) SAFEINT_ASSERT(x) + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator <<( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << bits ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator <<( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int << (U)bits ) ); + } + + // Left shift assignment + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator <<=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int <<= bits; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator <<=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int <<= (U)bits; + return *this; + } + + // Right shift + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator >>( U bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)( m_int >> bits ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator >>( SafeInt< U, E > bits ) const SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + return SafeInt< T, E >( (T)(m_int >> (U)bits) ); + } + + // Right shift assignment + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator >>=( U bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || bits >= 0 ); + ShiftAssert( bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int >>= bits; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator >>=( SafeInt< U, E > bits ) SAFEINT_NOTHROW + { + ShiftAssert( !std::numeric_limits< U >::is_signed || (U)bits >= 0 ); + ShiftAssert( (U)bits < (int)safeint_internal::int_traits< T >::bitCount ); + + m_int >>= (U)bits; + return *this; + } + + // Bitwise operators + // This only makes sense if we're dealing with the same type and size + // demand a type T, or something that fits into a type T + + // Bitwise & + _CONSTEXPR14 SafeInt< T, E > operator &( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( m_int & (T)rhs ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator &( U rhs ) const SAFEINT_NOTHROW + { + // we want to avoid setting bits by surprise + // consider the case of lhs = int, value = 0xffffffff + // rhs = char, value = 0xff + // + // programmer intent is to get only the lower 8 bits + // normal behavior is to upcast both sides to an int + // which then sign extends rhs, setting all the bits + + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ) ); + } + + // Bitwise & assignment + _CONSTEXPR14 SafeInt< T, E >& operator &=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int &= (T)rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator &=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, rhs ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator &=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( m_int, (U)rhs ); + return *this; + } + + // XOR + _CONSTEXPR14 SafeInt< T, E > operator ^( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int ^ (T)rhs ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator ^( U rhs ) const SAFEINT_NOTHROW + { + // If you land in the assert, this is because the bitwise operator + // was causing unexpected behavior. Fix is to properly cast your inputs + // so that it works like you meant, not unexpectedly + + return SafeInt< T, E >( BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ) ); + } + + // XOR assignment + _CONSTEXPR14 SafeInt< T, E >& operator ^=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int ^= (T)rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator ^=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, rhs ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator ^=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( m_int, (U)rhs ); + return *this; + } + + // bitwise OR + _CONSTEXPR14 SafeInt< T, E > operator |( SafeInt< T, E > rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( (T)( m_int | (T)rhs ) ); + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E > operator |( U rhs ) const SAFEINT_NOTHROW + { + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ) ); + } + + // bitwise OR assignment + _CONSTEXPR14 SafeInt< T, E >& operator |=( SafeInt< T, E > rhs ) SAFEINT_NOTHROW + { + m_int |= (T)rhs; + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator |=( U rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, rhs ); + return *this; + } + + template < typename U > + _CONSTEXPR14 SafeInt< T, E >& operator |=( SafeInt< U, E > rhs ) SAFEINT_NOTHROW + { + m_int = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( m_int, (U)rhs ); + return *this; + } + + // Miscellaneous helper functions + SafeInt< T, E > Min( SafeInt< T, E > test, const T floor = std::numeric_limits::min() ) const SAFEINT_NOTHROW + { + T tmp = test < m_int ? (T)test : m_int; + return tmp < floor ? floor : tmp; + } + + SafeInt< T, E > Max( SafeInt< T, E > test, const T upper = std::numeric_limits::max() ) const SAFEINT_NOTHROW + { + T tmp = test > m_int ? (T)test : m_int; + return tmp > upper ? upper : tmp; + } + + void Swap( SafeInt< T, E >& with ) SAFEINT_NOTHROW + { + T temp( m_int ); + m_int = with.m_int; + with.m_int = temp; + } + + static SafeInt< T, E > SafeAtoI( const char* input ) SAFEINT_CPP_THROW + { + return SafeTtoI( input ); + } + + static SafeInt< T, E > SafeWtoI( const wchar_t* input ) + { + return SafeTtoI( input ); + } + + enum alignBits + { + align2 = 1, + align4 = 2, + align8 = 3, + align16 = 4, + align32 = 5, + align64 = 6, + align128 = 7, + align256 = 8 + }; + + template < alignBits bits > + const SafeInt< T, E >& Align() SAFEINT_CPP_THROW + { + // Zero is always aligned + if( m_int == 0 ) + return *this; + + // We don't support aligning negative numbers at this time + // Can't align unsigned numbers on bitCount (e.g., 8 bits = 256, unsigned char max = 255) + // or signed numbers on bitCount-1 (e.g., 7 bits = 128, signed char max = 127). + // Also makes no sense to try to align on negative or no bits. + + ShiftAssert( ( ( std::numeric_limits< T >::is_signed && bits < (int)safeint_internal::int_traits< T >::bitCount - 1 ) + || ( !std::numeric_limits< T >::is_signed && bits < (int)safeint_internal::int_traits< T >::bitCount ) ) && + bits >= 0 && ( !std::numeric_limits< T >::is_signed || m_int > 0 ) ); + + const T AlignValue = ( (T)1 << bits ) - 1; + + m_int = (T)( ( m_int + AlignValue ) & ~AlignValue ); + + if( m_int <= 0 ) + E::SafeIntOnOverflow(); + + return *this; + } + + // Commonly needed alignments: + const SafeInt< T, E >& Align2() { return Align< align2 >(); } + const SafeInt< T, E >& Align4() { return Align< align4 >(); } + const SafeInt< T, E >& Align8() { return Align< align8 >(); } + const SafeInt< T, E >& Align16() { return Align< align16 >(); } + const SafeInt< T, E >& Align32() { return Align< align32 >(); } + const SafeInt< T, E >& Align64() { return Align< align64 >(); } +private: + + // This is almost certainly not the best optimized version of atoi, + // but it does not display a typical bug where it isn't possible to set MinInt + // and it won't allow you to overflow your integer. + // This is here because it is useful, and it is an example of what + // can be done easily with SafeInt. + template < typename U > + static SafeInt< T, E > SafeTtoI( U* input ) SAFEINT_CPP_THROW + { + U* tmp = input; + SafeInt< T, E > s; + bool negative = false; + + // Bad input, or empty string + if( input == nullptr || input[0] == 0 ) + E::SafeIntOnOverflow(); + + switch( *tmp ) + { + case '-': + tmp++; + negative = true; + break; + case '+': + tmp++; + break; + } + + while( *tmp != 0 ) + { + if( *tmp < '0' || *tmp > '9' ) + break; + + if( (T)s != 0 ) + s *= (T)10; + + if( !negative ) + s += (T)( *tmp - '0' ); + else + s -= (T)( *tmp - '0' ); + + tmp++; + } + + return s; + } + + T m_int; +}; + +// Helper function used to subtract pointers. +// Used to squelch warnings +template +_CONSTEXPR11 SafeInt SafePtrDiff(const P* p1, const P* p2) SAFEINT_CPP_THROW +{ + return SafeInt( p1 - p2 ); +} + +// Comparison operators + +//Less than +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <( SafeInt< U, E > lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, (U)lhs ); +} + +// Greater than +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// Greater than or equal +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >=( SafeInt lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( rhs, (T)lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator >=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( (U)rhs, (T)lhs ); +} + +// Less than or equal +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< U, T, ValidComparison< U, T >::method >::GreaterThan( lhs, (T)rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator <=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !GreaterThanTest< T, U, ValidComparison< T, U >::method >::GreaterThan( (T)lhs, (U)rhs ); +} + +// equality +// explicit overload for bool +template < typename T, typename E > +_CONSTEXPR11 bool operator ==( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return lhs == ( (T)rhs == 0 ? false : true ); +} + +template < typename T, typename E > +_CONSTEXPR11 bool operator ==( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return rhs == ( (T)lhs == 0 ? false : true ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator ==( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals((T)rhs, lhs); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator ==( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator ==( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, (U)rhs ); +} + +//not equals +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator !=( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)rhs, lhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator !=( SafeInt< T, E > lhs, U rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( (T)lhs, rhs ); +} + +template < typename T, typename U, typename E > +_CONSTEXPR11 bool operator !=( SafeInt< T, E > lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + return !EqualityTest< T, U, ValidComparison< T, U >::method >::IsEquals( lhs, rhs ); +} + + +template < typename T, typename E > +_CONSTEXPR11 bool operator !=( bool lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return ( (T)rhs == 0 ? false : true ) != lhs; +} + +template < typename T, typename E > +_CONSTEXPR11 bool operator !=( SafeInt< T, E > lhs, bool rhs ) SAFEINT_NOTHROW +{ + return ( (T)lhs == 0 ? false : true ) != rhs; +} + + +template < typename T, typename U, typename E, int method > class ModulusSimpleCaseHelper; + +template < typename T, typename E, int method > class ModulusSignedCaseHelper; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, true > +{ +public: + _CONSTEXPR14 static bool SignedCase( SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_NOTHROW + { + if( (T)rhs == (T)-1 ) + { + result = 0; + return true; + } + return false; + } +}; + +template < typename T, typename E > class ModulusSignedCaseHelper < T, E, false > +{ +public: + _CONSTEXPR11 static bool SignedCase( SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, true > +{ +public: + _CONSTEXPR14 static bool ModulusSimpleCase( U lhs, SafeInt< T, E > rhs, SafeInt< T, E >& result ) SAFEINT_CPP_THROW + { + if( rhs != 0 ) + { + if( ModulusSignedCaseHelper< T, E, std::numeric_limits< T >::is_signed >::SignedCase( rhs, result ) ) + return true; + + result = (T)( lhs % (T)rhs ); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template< typename T, typename U, typename E > +class ModulusSimpleCaseHelper < T, U, E, false > +{ +public: + _CONSTEXPR11 static bool ModulusSimpleCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt< T, E >& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Modulus +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator %( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Value of return depends on sign of lhs + // This one may not be safe - bounds check in constructor + // if lhs is negative and rhs is unsigned, this will throw an exception. + + // Fast-track the simple case + // same size and same sign + SafeInt< T, E > result; + + if( ModulusSimpleCaseHelper< T, U, E, (sizeof(T) == sizeof(U)) && ((bool)std::numeric_limits< T >::is_signed == (bool)std::numeric_limits< U >::is_signed) >::ModulusSimpleCase( lhs, rhs, result ) ) + return result; + + result = (SafeInt< U, E >(lhs) % (T)rhs); + return result; +} + +// Multiplication +template < typename T, typename U, typename E > +_CONSTEXPR14_MULTIPLY SafeInt< T, E > operator *( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >(ret); +} + +template < typename T, typename U, typename E, int method > class DivisionNegativeCornerCaseHelper; + +template < typename T, typename U, bool > class division_negative_negateU; + +template < typename T, typename U > class division_negative_negateU< T, U, true> +{ +public: + // sizeof(T) == 4 + _CONSTEXPR14 static U div(T rhs, U lhs) { return lhs / (U)(~(std::uint32_t)(T)rhs + 1); } +}; + +template < typename T, typename U > class division_negative_negateU< T, U, false> +{ +public: + _CONSTEXPR14 static U div(T rhs, U lhs) { return lhs / (U)(~(std::uint64_t)(T)rhs + 1); } +}; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, true > +{ +public: + static bool NegativeCornerCase( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + // Problem case - normal casting behavior changes meaning + // flip rhs to positive + // any operator casts now do the right thing + U tmp = division_negative_negateU< T, U, sizeof(T) == 4>::div(rhs, lhs); + + if( tmp <= (U)std::numeric_limits::max() ) + { + result = SafeInt< T, E >( (T)(~(std::uint64_t)tmp + 1) ); + return true; + } + + // Corner case + T maxT = std::numeric_limits::max(); + if( tmp == (U)maxT + 1 ) + { + T minT = std::numeric_limits::min(); + result = SafeInt< T, E >( minT ); + return true; + } + + E::SafeIntOnOverflow(); + } +}; + +template < typename T, typename U, typename E > class DivisionNegativeCornerCaseHelper< T, U, E, false > +{ +public: + _CONSTEXPR11 static bool NegativeCornerCase( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, true > +{ +public: + _CONSTEXPR14 static bool DivisionCornerCase1( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( (T)rhs > 0 ) + { + result = SafeInt< T, E >( lhs/(T)rhs ); + return true; + } + + // Now rhs is either negative, or zero + if( (T)rhs != 0 ) + { + if( DivisionNegativeCornerCaseHelper< T, U, E, sizeof( U ) >= 4 && sizeof( T ) <= sizeof( U ) >::NegativeCornerCase( lhs, rhs, result ) ) + return true; + + result = SafeInt< T, E >(lhs/(T)rhs); + return true; + } + + E::SafeIntOnDivZero(); + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper < T, U, E, false > +{ +public: + _CONSTEXPR11 static bool DivisionCornerCase1( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +template < typename T, typename U, typename E, int method > class DivisionCornerCaseHelper2; + +template < typename T, typename U, bool > class div_negate_min; + +template < typename T, typename U > class div_negate_min < T, U , true > +{ +public: + _CONSTEXPR14 static bool Value(T& ret) + { + ret = (T)(-(T)std::numeric_limits< U >::min()); + return true; + } +}; + +template < typename T, typename U > class div_negate_min < T, U, false > +{ +public: + _CONSTEXPR14 static bool Value(T& ) + { + return false; + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, true > +{ +public: + _CONSTEXPR14 static bool DivisionCornerCase2( U lhs, SafeInt< T, E > rhs, SafeInt& result ) SAFEINT_CPP_THROW + { + if( lhs == std::numeric_limits< U >::min() && (T)rhs == -1 ) + { + // corner case of a corner case - lhs = min int, rhs = -1, + // but rhs is the return type, so in essence, we can return -lhs + // if rhs is a larger type than lhs + // If types are wrong, throws + T tmp = 0; + + if (div_negate_min< T, U, sizeof(U) < sizeof(T) > ::Value(tmp)) + result = tmp; + else + E::SafeIntOnOverflow(); + + return true; + } + + return false; + } +}; + +template < typename T, typename U, typename E > class DivisionCornerCaseHelper2 < T, U, E, false > +{ +public: + _CONSTEXPR11 static bool DivisionCornerCase2( U /*lhs*/, SafeInt< T, E > /*rhs*/, SafeInt& /*result*/ ) SAFEINT_NOTHROW + { + return false; + } +}; + +// Division +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator /( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + // Corner case - has to be handled seperately + SafeInt< T, E > result; + if( DivisionCornerCaseHelper< T, U, E, (int)DivisionMethod< U, T >::method == (int)DivisionState_UnsignedSigned >::DivisionCornerCase1( lhs, rhs, result ) ) + return result; + + if( DivisionCornerCaseHelper2< T, U, E, safeint_internal::type_compare< T, U >::isBothSigned >::DivisionCornerCase2( lhs, rhs, result ) ) + return result; + + // Otherwise normal logic works with addition of bounds check when casting from U->T + U ret = 0; + DivisionHelper< U, T, DivisionMethod< U, T >::method >::template DivideThrow< E >( lhs, (T)rhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Addition +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator +( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( (T)rhs, lhs, ret ); + return SafeInt< T, E >( ret ); +} + +// Subtraction +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< T, E > operator -( U lhs, SafeInt< T, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< U, T, SubtractionMethod2< U, T >::method >::template SubtractThrow< E >( lhs, rhs.Ref(), ret ); + + return SafeInt< T, E >( ret ); +} + +// Overrides designed to deal with cases where a SafeInt is assigned out +// to a normal int - this at least makes the last operation safe +// += +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator +=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + AdditionHelper< T, U, AdditionMethod< T, U >::method >::template AdditionThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator -=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + SubtractionHelper< T, U, SubtractionMethod< T, U >::method >::template SubtractThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator *=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + MultiplicationHelper< T, U, MultiplicationMethod< T, U >::method >::template MultiplyThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator /=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + DivisionHelper< T, U, DivisionMethod< T, U >::method >::template DivideThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator %=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + T ret( 0 ); + ModulusHelper< T, U, ValidComparison< T, U >::method >::template ModulusThrow< E >( lhs, (U)rhs, ret ); + lhs = ret; + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator &=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator ^=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator |=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( lhs, (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator <<=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) << (U)rhs ); + return lhs; +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T& operator >>=( T& lhs, SafeInt< U, E > rhs ) SAFEINT_NOTHROW +{ + lhs = (T)( SafeInt< T, E >( lhs ) >> (U)rhs ); + return lhs; +} + +// Specific pointer overrides +// Note - this function makes no attempt to ensure +// that the resulting pointer is still in the buffer, only +// that no int overflows happened on the way to getting the new pointer +template < typename T, typename U, typename E > +T*& operator +=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + // Note: this doesn't really make sense as a constexpr, but cannot be because of the reinterpret_cast + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // Check first that rhs is valid for the type of ptrdiff_t + // and that multiplying by sizeof( T ) doesn't overflow a ptrdiff_t + // Next, we need to add 2 SafeInts of different types, so unbox the ptr_diff + // Finally, cast the number back to a pointer of the correct type + lhs = reinterpret_cast< T* >( (size_t)( ptr_val + (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator -=( T*& lhs, SafeInt< U, E > rhs ) SAFEINT_CPP_THROW +{ + // Cast the pointer to a number so we can do arithmetic + SafeInt< size_t, E > ptr_val = reinterpret_cast< size_t >( lhs ); + // See above for comments + lhs = reinterpret_cast< T* >( (size_t)( ptr_val - (ptrdiff_t)( SafeInt< ptrdiff_t, E >( rhs ) * sizeof( T ) ) ) ); + return lhs; +} + +template < typename T, typename U, typename E > +T*& operator *=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert( sizeof(T) == 0, "Unsupported operator" ); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator /=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator %=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator &=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator ^=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +T*& operator |=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T*& operator <<=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +template < typename T, typename U, typename E > +_CONSTEXPR14 T*& operator >>=( T*& lhs, SafeInt< U, E > ) SAFEINT_NOTHROW +{ + // This operator explicitly not supported + static_assert(sizeof(T) == 0, "Unsupported operator"); + return (lhs = nullptr); +} + +// Shift operators +// NOTE - shift operators always return the type of the lhs argument + +// Left shift +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< U, E > operator <<( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !std::numeric_limits< T >::is_signed || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)safeint_internal::int_traits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs << (T)bits ) ); +} + +// Right shift +template < typename T, typename U, typename E > +_CONSTEXPR14 SafeInt< U, E > operator >>( U lhs, SafeInt< T, E > bits ) SAFEINT_NOTHROW +{ + ShiftAssert( !std::numeric_limits< T >::is_signed || (T)bits >= 0 ); + ShiftAssert( (T)bits < (int)safeint_internal::int_traits< U >::bitCount ); + + return SafeInt< U, E >( (U)( lhs >> (T)bits ) ); +} + +// Bitwise operators +// This only makes sense if we're dealing with the same type and size +// demand a type T, or something that fits into a type T. + +// Bitwise & +template < typename T, typename U, typename E > +_CONSTEXPR11 SafeInt< T, E > operator &( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryAndHelper< T, U, BinaryMethod< T, U >::method >::And( (T)rhs, lhs ) ); +} + +// Bitwise XOR +template < typename T, typename U, typename E > +_CONSTEXPR11 SafeInt< T, E > operator ^( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >(BinaryXorHelper< T, U, BinaryMethod< T, U >::method >::Xor( (T)rhs, lhs ) ); +} + +// Bitwise OR +template < typename T, typename U, typename E > +_CONSTEXPR11 SafeInt< T, E > operator |( U lhs, SafeInt< T, E > rhs ) SAFEINT_NOTHROW +{ + return SafeInt< T, E >( BinaryOrHelper< T, U, BinaryMethod< T, U >::method >::Or( (T)rhs, lhs ) ); +} + +#endif //SAFEINT_HPP + +#if defined VISUAL_STUDIO_SAFEINT_COMPAT +} // utilities +} // msl +#endif + diff --git a/hilti/include/3rdparty/SafeInt/SafeInt.patch b/hilti/include/3rdparty/SafeInt/SafeInt.patch new file mode 100644 index 000000000..ae82fbcd0 --- /dev/null +++ b/hilti/include/3rdparty/SafeInt/SafeInt.patch @@ -0,0 +1,18 @@ +--- ../3rdparty/SafeInt/SafeInt.hpp.orig 2019-01-21 04:44:44.723753013 +0000 ++++ ../3rdparty/SafeInt/SafeInt.hpp 2019-01-21 04:45:12.687599562 +0000 +@@ -5815,6 +5815,7 @@ + const T* Ptr() const SAFEINT_NOTHROW { return &m_int; } + _CONSTEXPR14 const T& Ref() const SAFEINT_NOTHROW { return m_int; } + ++#if 0 + // Or if SafeInt< T, E >::Ptr() is inconvenient, use the overload + // operator & + // This allows you to do unsafe things! +@@ -5822,6 +5823,7 @@ + // pass a SafeInt into things like ReadFile + T* operator &() SAFEINT_NOTHROW { return &m_int; } + const T* operator &() const SAFEINT_NOTHROW { return &m_int; } ++#endif + + // Unary operators + _CONSTEXPR11 bool operator !() const SAFEINT_NOTHROW { return (!m_int) ? true : false; } diff --git a/hilti/include/3rdparty/enum-class/.gitignore b/hilti/include/3rdparty/enum-class/.gitignore new file mode 100644 index 000000000..ee35e1a6b --- /dev/null +++ b/hilti/include/3rdparty/enum-class/.gitignore @@ -0,0 +1,2 @@ +xcuserdata +xcuserdata/* diff --git a/hilti/include/3rdparty/enum-class/CMakeLists.txt b/hilti/include/3rdparty/enum-class/CMakeLists.txt new file mode 100644 index 000000000..bcfbbcade --- /dev/null +++ b/hilti/include/3rdparty/enum-class/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.8) + +project(EnumClass) + +add_executable(enumclass main.cpp) +target_compile_features(enumclass + PRIVATE + cxx_auto_type + cxx_relaxed_constexpr + cxx_static_assert + cxx_strong_enums + cxx_uniform_initialization +) diff --git a/hilti/include/3rdparty/enum-class/EnumClass.h b/hilti/include/3rdparty/enum-class/EnumClass.h new file mode 100644 index 000000000..4d36c0bd9 --- /dev/null +++ b/hilti/include/3rdparty/enum-class/EnumClass.h @@ -0,0 +1,752 @@ +// +// EnumClass.h +// ArticleEnumClass +// +// Created by Gabriel Aubut-Lussier on 17-08-07. +// Copyright © 2017 Gabriel Aubut-Lussier. All rights reserved. +// + +#ifndef EnumClass_h +#define EnumClass_h + +#include +#include + +template +struct enable_enum_class_bitmask +{ + static constexpr bool value = false; +}; + +#define enableEnumClassBitmask(T) template<> \ +struct enable_enum_class_bitmask \ +{ \ +static constexpr bool value = true; \ +} + +/** + * Wrapper for an enumerator that provides implicit bool conversion + */ +template +struct enumerator +{ + constexpr enumerator(const T& value) : value(value) {} + constexpr explicit operator bool() const + { + using underlying_type = typename std::underlying_type::type; + return static_cast(value) != 0; + } + constexpr operator T() const + { + return value; + } + + T value; +}; + +/** + * Wrapper that differentiates combined enumerators from a single enumerator + * to provent accidental comparisons between a bitmask and a single enumerator + * using operator== or operator!= + */ +template +struct bitmask +{ + using underlying_type = typename std::underlying_type::type; + + constexpr bitmask(const T& value) : value(static_cast(value)) {} + constexpr bitmask(const enumerator& enumerator) : value(static_cast(enumerator.value)) {} + constexpr explicit operator bool() const + { + return value != 0; + } + + underlying_type value; +}; + +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +make_bitmask(const T& t) +{ + return bitmask{t}; +} + +/** + * operator&(T, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const T& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + assert((static_cast(lhs) & (static_cast(lhs) - 1)) == 0); + return enumerator{static_cast(static_cast(lhs) & static_cast(rhs))}; +} + +/** + * operator&(enumerator, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const enumerator& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(static_cast(lhs.value) & static_cast(rhs.value))}; +} + +/** + * operator&(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator&(const bitmask& lhs, const bitmask& rhs) +{ + return bitmask{static_cast(lhs.value & rhs.value)}; +} + +/** + * operator&(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const bitmask& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(lhs.value & static_cast(rhs))}; +} + +/** + * operator&(T, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const T& lhs, const bitmask& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(static_cast(lhs) & rhs.value)}; +} + +/** + * operator&(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const bitmask& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(lhs.value & static_cast(rhs.value))}; +} + +/** + * operator&(enumerator, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const enumerator& lhs, const bitmask& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(static_cast(lhs.value) & rhs.value)}; +} + +/** + * operator&(T, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const T& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(static_cast(lhs) & static_cast(rhs.value))}; +} + +/** + * operator&(enumerator, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, enumerator>::type +operator&(const enumerator& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return enumerator{static_cast(static_cast(lhs.value) & static_cast(rhs))}; +} + +/** + * operator|(T, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const T& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs) | static_cast(rhs))}; +} + +/** + * operator|(enumerator, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const enumerator& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs.value) | static_cast(rhs.value))}; +} + +/** + * operator|(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const bitmask& lhs, const bitmask& rhs) +{ + return bitmask{static_cast(lhs.value | rhs.value)}; +} + +/** + * operator|(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const bitmask& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(lhs.value | static_cast(rhs))}; +} + +/** + * operator|(T, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const T& lhs, const bitmask& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs) | rhs.value)}; +} + +/** + * operator|(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const bitmask& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(lhs.value | static_cast(rhs.value))}; +} + +/** + * operator|(enumerator, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const enumerator& lhs, const bitmask& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs.value) | rhs.value)}; +} + +/** + * operator|(enumerator, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const enumerator& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs.value) | static_cast(rhs))}; +} + +/** + * operator|(T, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator|(const T& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs) | static_cast(rhs.value))}; +} + +/** + * operator^(T, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const T& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs) ^ static_cast(rhs))}; +} + +/** + * operator^(enumerator, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const enumerator& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs.value) ^ static_cast(rhs.value))}; +} + +/** + * operator^(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const bitmask& lhs, const bitmask& rhs) +{ + return bitmask{static_cast(lhs.value ^ rhs.value)}; +} + +/** + * operator^(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const bitmask& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(lhs.value ^ static_cast(rhs))}; +} + +/** + * operator^(T, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const T& lhs, const bitmask& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs) ^ rhs.value)}; +} + +/** + * operator^(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const bitmask& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(lhs.value ^ static_cast(rhs.value))}; +} + +/** + * operator^(enumerator, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const enumerator& lhs, const bitmask& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs.value) ^ rhs.value)}; +} + +/** + * operator^(enumerator, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const enumerator& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs.value) ^ static_cast(rhs))}; +} + +/** + * operator^(T, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator^(const T& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(static_cast(lhs) ^ static_cast(rhs.value))}; +} + +/** + * operator~(T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator~(const T& value) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(~ static_cast(value))}; +} + +/** + * operator~(enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator~(const enumerator& lhs) +{ + using underlying_type = typename std::underlying_type::type; + return bitmask{static_cast(~ static_cast(lhs.value))}; +} + +/** + * operator~(bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask>::type +operator~(const bitmask& lhs) +{ + return bitmask{static_cast(~ lhs.value)}; +} + +/** + * operator&=(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator&=(bitmask& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + lhs.value &= static_cast(rhs); + return lhs; +} + +/** + * operator&=(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator&=(bitmask& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + lhs.value &= static_cast(rhs.value); + return lhs; +} + +/** + * operator&=(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator&=(bitmask& lhs, const bitmask& rhs) +{ + lhs.value &= rhs.value; + return lhs; +} + +/** + * operator|=(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator|=(bitmask& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + lhs.value |= static_cast(rhs); + return lhs; +} + +/** + * operator|=(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator|=(bitmask& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + lhs.value |= static_cast(rhs.value); + return lhs; +} + +/** + * operator|=(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator|=(bitmask& lhs, const bitmask& rhs) +{ + lhs.value |= rhs.value; + return lhs; +} + +/** + * operator^=(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator^=(bitmask& lhs, const T& rhs) +{ + using underlying_type = typename std::underlying_type::type; + lhs.value ^= static_cast(rhs); + return lhs; +} + +/** + * operator^=(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator^=(bitmask& lhs, const enumerator& rhs) +{ + using underlying_type = typename std::underlying_type::type; + lhs.value ^= static_cast(rhs.value); + return lhs; +} + +/** + * operator^=(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bitmask&>::type +operator^=(bitmask& lhs, const bitmask& rhs) +{ + lhs.value ^= rhs.value; + return lhs; +} + +/** + * operator==(T, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const T& lhs, const T& rhs) +{ + return lhs == rhs; +} + +/** + * operator==(enumerator, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const enumerator& lhs, const enumerator& rhs) +{ + return lhs.value == rhs.value; +} + +/** + * operator==(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const bitmask& lhs, const bitmask& rhs) +{ + return lhs.value == rhs.value; +} + +/** + * operator==(enumerator, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const enumerator& lhs, const T& rhs) +{ + return lhs.value == rhs; +} + +/** + * operator==(T, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const T& lhs, const enumerator& rhs) +{ + return lhs == rhs.value; +} + +/** + * operator==(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const bitmask& /* lhs */, const T& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator==(T, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const T& /* lhs */, const bitmask& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator==(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const bitmask& /* lhs */, const enumerator& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator==(enumerator, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator==(const enumerator& /* lhs */, const bitmask& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator!=(T, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const T& lhs, const T& rhs) +{ + return lhs != rhs; +} + +/** + * operator!=(enumerator, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const enumerator& lhs, const enumerator& rhs) +{ + return lhs.value != rhs.value; +} + +/** + * operator!=(bitmask, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const bitmask& lhs, const bitmask& rhs) +{ + return lhs.value != rhs.value; +} + +/** + * operator!=(enumerator, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const enumerator& lhs, const T& rhs) +{ + return lhs.value != rhs; +} + +/** + * operator!=(T, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const T& lhs, const enumerator& rhs) +{ + return lhs != rhs.value; +} + +/** + * operator!=(bitmask, T) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const bitmask& /* lhs */, const T& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator!=(T, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const T& /* lhs */, const bitmask& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator!=(bitmask, enumerator) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const bitmask& /* lhs */, const enumerator& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +/** + * operator!=(enumerator, bitmask) + */ +template +constexpr +typename std::enable_if::value && enable_enum_class_bitmask::value, bool>::type +operator!=(const enumerator& /* lhs */, const bitmask& /* rhs */) +{ + static_assert(!std::is_same::underlying_type, typename std::underlying_type::type>::value, "A bitmask can't be compared to an enumerator. Use & first."); + return false; +} + +#endif /* EnumClass_h */ diff --git a/hilti/include/3rdparty/enum-class/LICENSE b/hilti/include/3rdparty/enum-class/LICENSE new file mode 100644 index 000000000..407065841 --- /dev/null +++ b/hilti/include/3rdparty/enum-class/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2017, Gabriel Aubut-Lussier +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. + +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 HOLDER 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. diff --git a/hilti/include/3rdparty/enum-class/README.hilti b/hilti/include/3rdparty/enum-class/README.hilti new file mode 100644 index 000000000..100395c81 --- /dev/null +++ b/hilti/include/3rdparty/enum-class/README.hilti @@ -0,0 +1 @@ +From https://github.com/Dalzhim/ArticleEnumClass-v2 diff --git a/hilti/include/3rdparty/enum-class/Readme.md b/hilti/include/3rdparty/enum-class/Readme.md new file mode 100644 index 000000000..6ab0574e4 --- /dev/null +++ b/hilti/include/3rdparty/enum-class/Readme.md @@ -0,0 +1,39 @@ +# Bitmask operators and typesafe comparisons for enum class +*EnumClass.h* is a utility header that allows you to easily generate bitwise operators for your custom `enum class` type like so: +```cpp +enum class myEnum +{ + enumerator1 = 0x1 << 0, + enumerator2 = 0x1 << 1, + enumerator3 = 0x1 << 2 +}; + +enableEnumClassBitmask(myEnum); // Activate bitmask operators +``` +This utility relies on two concepts: enumerators and masks. An enumerator’s purpose is to give a name to a specific bit when it is set. A mask, on the other hand, represents the state of every bit (and this way, of every enumerator), whether they are set or cleared. Comparing an enumerator to a mask using `operator==` or `operator!=` is a compiler error. [A complete blog post](https://dalzhim.github.io/2017/08/11/Improving-the-enum-class-bitmask/) explains why and how this is implemented. + +Here are some tables that summarize the return type of all of the operators : + +#### Binary bitwise operators + + | **`E, E`** | **`E`, `bitmask`** | **`bitmask`, `E`** | **`bitmask`, `bitmask`** + | ---------- | --------------------- | --------------------- | ------------------------------ +**`operator&`** | `E` | `E` | `E` | `bitmask` +**`operator\|`** | `bitmask` | `bitmask` | `bitmask` | `bitmask` +**`operator^`** | `bitmask` | `bitmask` | `bitmask` | `bitmask` +**`operator&=`** | `bitmask` | `bitmask` | `bitmask` | `bitmask` +**`operator\|=`** | `bitmask` | `bitmask` | `bitmask` | `bitmask` +**`operator^=`** | `bitmask` | `bitmask` | `bitmask` | `bitmask` + +#### Unary bitwise operators + + | **`E`** | **`bitmask`** + | ------- | ---------------- +**`operator~`** | `bitmask` | `bitmask` + +#### Comparison operators + + | **`E, E`** | **`E`, `bitmask`** | **`bitmask`, `E`** | **`bitmask`, `bitmask`** + | ---------- | --------------------- | --------------------- | ------------------------------ +**`operator==`** | `bool` | `static_assert` | `static_assert` | `bool` +**`operator!=`** | `bool` | `static_assert` | `static_assert` | `bool` diff --git a/hilti/include/3rdparty/enum-class/main.cpp b/hilti/include/3rdparty/enum-class/main.cpp new file mode 100644 index 000000000..3e411626c --- /dev/null +++ b/hilti/include/3rdparty/enum-class/main.cpp @@ -0,0 +1,145 @@ +// +// main.cpp +// ArticleEnumClass_v2 +// +// Created by Gabriel Aubut-Lussier on 17-08-07. +// Copyright © 2017 Gabriel Aubut-Lussier. All rights reserved. +// + +#include +#include "EnumClass.h" +#include + +enum class eTest +{ + kEnumerator1 = 1 << 0, + kEnumerator2 = 1 << 1, + kEnumerator3 = 1 << 2 +}; +enableEnumClassBitmask(eTest); + +constexpr bool f(eTest e) {return e != static_cast(0);} +constexpr bool g(enumerator e) {return bool(e);} +constexpr bool h(bitmask e) {return bool(e);} + +int main(int argc, const char * argv[]) +{ + constexpr auto t = eTest::kEnumerator1; + constexpr auto enumerator = ::enumerator{t}; + constexpr auto bitmask = t | eTest::kEnumerator2; + + /** + * operator& + */ + static_assert(t & t, "operator&(T, T)"); + static_assert(enumerator & enumerator, "operator&(enumerator, enumerator)"); + static_assert(bitmask & bitmask, "operator&(bitmask, bitmask)"); + static_assert(t & enumerator, "operator&(T, enumerator)"); + static_assert(enumerator & t, "operator&(enumerator, T)"); + static_assert(t & bitmask, "operator&(T, bitmask)"); + static_assert(bitmask & t, "operator&(bitmask, T)"); + static_assert(enumerator & bitmask, "operator&(enumerator, bitmask)"); + static_assert(bitmask & enumerator, "operator&(bitmask, enumerator)"); + + /** + * operator| + */ + static_assert(enumerator | enumerator, "operator|(T, T)"); + static_assert(enumerator | enumerator, "operator|(enumerator, enumerator)"); + static_assert(bitmask | bitmask, "operator|(bitmask, bitmask)"); + static_assert(t | enumerator, "operator|(T, enumerator)"); + static_assert(enumerator | t, "operator|(enumerator, T)"); + static_assert(t | bitmask, "operator|(T, bitmask)"); + static_assert(bitmask | t, "operator|(bitmask, T)"); + static_assert(enumerator | bitmask, "operator|(enumerator, bitmask)"); + static_assert(bitmask | enumerator, "operator|(bitmask, enumerator)"); + + /** + * operator^ + */ + static_assert(!bool(t ^ t), "operator^(T, T)"); + static_assert(!bool(enumerator ^ enumerator), "operator^(enumerator, enumerator)"); + static_assert(!bool(bitmask ^ bitmask), "operator^(bitmask, bitmask)"); + static_assert(!bool(t ^ enumerator), "operator^(T, enumerator)"); + static_assert(!bool(enumerator ^ t), "operator^(enumerator, T)"); + static_assert(t ^ bitmask, "operator^(T, bitmask)"); + static_assert(bitmask ^ t, "operator^(bitmask, T)"); + static_assert(enumerator ^ bitmask, "operator^(enumerator, bitmask)"); + static_assert(bitmask ^ enumerator, "operator^(bitmask, enumerator)"); + + /** + * operator~ + */ + static_assert(~t, "operator~(T)"); + static_assert(~enumerator, "operator~(enumerator)"); + static_assert(~bitmask, "operator~(bitmask)"); + + /** + * operator&= + */ + auto mutbitmask = bitmask; + mutbitmask &= t; + mutbitmask &= enumerator; + mutbitmask &= mutbitmask; + assert(bitmask); + + /** + * operator|= + */ + mutbitmask |= t; + mutbitmask |= enumerator; + mutbitmask |= mutbitmask; + assert(mutbitmask); + + /** + * operator^= + */ + mutbitmask ^= t; + mutbitmask ^= enumerator; + mutbitmask ^= mutbitmask; + assert(!mutbitmask); + + /** + * bitmask::operator bool + */ + assert(!bool(mutbitmask)); + + /** + * operator== + */ + static_assert(t == t, "operator==(T, T)"); + static_assert(enumerator == enumerator, "operator==(enumerator, enumerator)"); + static_assert(bitmask == bitmask, "operator==(bitmask, bitmask)"); + static_assert(t == enumerator, "operator==(T, enumerator)"); + static_assert(enumerator == t, "operator==(enumerator, T)"); +// static_assert(t == bitmask, "operator==(T, bitmask)"); +// static_assert(bitmask == t, "operator==(bitmask, T)"); +// static_assert(enumerator == bitmask, "operator==(enumerator, bitmask)"); +// static_assert(bitmask == enumerator, "operator==(bitmask, enumerator)"); + + /** + * operator!= + */ + static_assert(!(t != t), "operator!=(T, T)"); + static_assert(!(enumerator != enumerator), "operator!=(enumerator, enumerator)"); + static_assert(!(bitmask != bitmask), "operator!=(bitmask, bitmask)"); + static_assert(!(t != enumerator), "operator!=(T, enumerator)"); + static_assert(!(enumerator != t), "operator!=(enumerator, T)"); +// static_assert(t != bitmask, "operator!=(T, bitmask)"); +// static_assert(bitmask != t, "operator!=(bitmask, T)"); +// static_assert(enumerator != bitmask, "operator!=(enumerator, bitmask)"); +// static_assert(bitmask != enumerator, "operator!=(bitmask, enumerator)"); + + static_assert(enumerator, "enumerator::operator bool()"); + static_assert(bitmask, "bitmask::operator bool()"); + + static_assert(f(t), "no conversion"); + static_assert(f(enumerator), "enumerator::operator T() implicit conversion"); + static_assert(g(t), "enumerator(T) implicit conversion"); + static_assert(h(t), "bitmask(T) implicit conversion"); + static_assert(h(enumerator), "bitmask(enumerator) implicit conversion"); + + static_assert(g(bitmask & t), "It must be possible to isolate a single enumerator from a bitmask"); + + static_assert(make_bitmask(t) == ::bitmask{t}, "Must be able to easily make a bitmask from a single enumerator"); +} diff --git a/hilti/include/3rdparty/libtask/386-ucontext.h b/hilti/include/3rdparty/libtask/386-ucontext.h new file mode 100644 index 000000000..2faa4aaa7 --- /dev/null +++ b/hilti/include/3rdparty/libtask/386-ucontext.h @@ -0,0 +1,121 @@ +#define setcontext(u) setmcontext(&(u)->uc_mcontext) +#define getcontext(u) getmcontext(&(u)->uc_mcontext) +typedef struct mcontext mcontext_t; +typedef struct ucontext ucontext_t; + +extern int swapcontext(ucontext_t*, const ucontext_t*); +extern void makecontext(ucontext_t*, void(*)(), int, ...); +extern int getmcontext(mcontext_t*); +extern void setmcontext(const mcontext_t*); + +/*- + * Copyright (c) 1999 Marcel Moolenaar + * 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 + * in this position and unchanged. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ + */ + +/* #include */ + +/*- + * Copyright (c) 1999 Marcel Moolenaar + * 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 + * in this position and unchanged. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ + */ + +struct mcontext { + /* + * The first 20 fields must match the definition of + * sigcontext. So that we can support sigcontext + * and ucontext_t at the same time. + */ + int mc_onstack; /* XXX - sigcontext compat. */ + int mc_gs; + int mc_fs; + int mc_es; + int mc_ds; + int mc_edi; + int mc_esi; + int mc_ebp; + int mc_isp; + int mc_ebx; + int mc_edx; + int mc_ecx; + int mc_eax; + int mc_trapno; + int mc_err; + int mc_eip; + int mc_cs; + int mc_eflags; + int mc_esp; /* machine state */ + int mc_ss; + + int mc_fpregs[28]; /* env87 + fpacc87 + u_long */ + int __spare__[17]; +}; + +struct ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + mcontext_t uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int __spare__[8]; +}; + + diff --git a/hilti/include/3rdparty/libtask/COPYRIGHT b/hilti/include/3rdparty/libtask/COPYRIGHT new file mode 100644 index 000000000..45fcbc0e1 --- /dev/null +++ b/hilti/include/3rdparty/libtask/COPYRIGHT @@ -0,0 +1,43 @@ + +This software was developed as part of a project at MIT. + +Copyright (c) 2005-2007 Russ Cox, + Massachusetts Institute of Technology + +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. + +=== + +Contains parts of an earlier library that has: + +/* + * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox + * Copyright (c) 2003 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + diff --git a/hilti/include/3rdparty/libtask/amd64-ucontext.h b/hilti/include/3rdparty/libtask/amd64-ucontext.h new file mode 100644 index 000000000..48cb0c7ce --- /dev/null +++ b/hilti/include/3rdparty/libtask/amd64-ucontext.h @@ -0,0 +1,137 @@ +#define setcontext(u) setmcontext(&(u)->uc_mcontext) +#define getcontext(u) getmcontext(&(u)->uc_mcontext) +typedef struct mcontext mcontext_t; +typedef struct ucontext ucontext_t; + +extern int swapcontext(ucontext_t*, const ucontext_t*); +extern void makecontext(ucontext_t*, void(*)(), int, ...); +extern int getmcontext(mcontext_t*); +extern void setmcontext(const mcontext_t*); + +/*- + * Copyright (c) 1999 Marcel Moolenaar + * 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 + * in this position and unchanged. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD: src/sys/sys/ucontext.h,v 1.4 1999/10/11 20:33:17 luoqi Exp $ + */ + +/* #include */ + +/*- + * Copyright (c) 1999 Marcel Moolenaar + * 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 + * in this position and unchanged. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ + */ + +struct mcontext { + /* + * The first 20 fields must match the definition of + * sigcontext. So that we can support sigcontext + * and ucontext_t at the same time. + */ + long mc_onstack; /* XXX - sigcontext compat. */ + long mc_rdi; /* machine state (struct trapframe) */ + long mc_rsi; + long mc_rdx; + long mc_rcx; + long mc_r8; + long mc_r9; + long mc_rax; + long mc_rbx; + long mc_rbp; + long mc_r10; + long mc_r11; + long mc_r12; + long mc_r13; + long mc_r14; + long mc_r15; + long mc_trapno; + long mc_addr; + long mc_flags; + long mc_err; + long mc_rip; + long mc_cs; + long mc_rflags; + long mc_rsp; + long mc_ss; + + long mc_len; /* sizeof(mcontext_t) */ +#define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ +#define _MC_FPFMT_XMM 0x10002 + long mc_fpformat; +#define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ +#define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ +#define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ + long mc_ownedfp; + /* + * See for the internals of mc_fpstate[]. + */ + long mc_fpstate[64]; + long mc_spare[8]; +}; + +struct ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + mcontext_t uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int __spare__[8]; +}; + + diff --git a/hilti/include/3rdparty/libtask/mips-ucontext.h b/hilti/include/3rdparty/libtask/mips-ucontext.h new file mode 100644 index 000000000..4d4429e70 --- /dev/null +++ b/hilti/include/3rdparty/libtask/mips-ucontext.h @@ -0,0 +1,77 @@ +typedef struct mcontext mcontext_t; +typedef struct ucontext ucontext_t; + +extern int swapcontext(ucontext_t*, const ucontext_t*); +extern void makecontext(ucontext_t*, void(*)(), int, ...); + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)ucontext.h 8.1 (Berkeley) 6/10/93 + * JNPR: ucontext.h,v 1.2 2007/08/09 11:23:32 katta + * $FreeBSD: src/sys/mips/include/ucontext.h,v 1.2 2010/01/10 19:50:24 imp Exp $ + */ + +struct mcontext { + /* + * These fields must match the corresponding fields in struct + * sigcontext which follow 'sc_mask'. That way we can support + * struct sigcontext and ucontext_t at the same time. + */ + int mc_onstack; /* sigstack state to restore */ + int mc_pc; /* pc at time of signal */ + int mc_regs[32]; /* processor regs 0 to 31 */ + int sr; /* status register */ + int mullo, mulhi; /* mullo and mulhi registers... */ + int mc_fpused; /* fp has been used */ + int mc_fpregs[33]; /* fp regs 0 to 31 and csr */ + int mc_fpc_eir; /* fp exception instruction reg */ + void *mc_tls; /* pointer to TLS area */ + int __spare__[8]; /* XXX reserved */ +}; + +struct ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + mcontext_t uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int uc_flags; + int __spare__[4]; +}; diff --git a/hilti/include/3rdparty/libtask/power-ucontext.h b/hilti/include/3rdparty/libtask/power-ucontext.h new file mode 100644 index 000000000..691f0f540 --- /dev/null +++ b/hilti/include/3rdparty/libtask/power-ucontext.h @@ -0,0 +1,37 @@ +#define setcontext(u) _setmcontext(&(u)->mc) +#define getcontext(u) _getmcontext(&(u)->mc) +typedef struct mcontext mcontext_t; +typedef struct ucontext ucontext_t; +struct mcontext +{ + ulong pc; /* lr */ + ulong cr; /* mfcr */ + ulong ctr; /* mfcr */ + ulong xer; /* mfcr */ + ulong sp; /* callee saved: r1 */ + ulong toc; /* callee saved: r2 */ + ulong r3; /* first arg to function, return register: r3 */ + ulong gpr[19]; /* callee saved: r13-r31 */ +/* +// XXX: currently do not save vector registers or floating-point state +// ulong pad; +// uvlong fpr[18]; / * callee saved: f14-f31 * / +// ulong vr[4*12]; / * callee saved: v20-v31, 256-bits each * / +*/ +}; + +struct ucontext +{ + struct { + void *ss_sp; + uint ss_size; + } uc_stack; + sigset_t uc_sigmask; + mcontext_t mc; +}; + +void makecontext(ucontext_t*, void(*)(void), int, ...); +int swapcontext(ucontext_t*, const ucontext_t*); +int _getmcontext(mcontext_t*); +void _setmcontext(const mcontext_t*); + diff --git a/hilti/include/3rdparty/libtask/task.h b/hilti/include/3rdparty/libtask/task.h new file mode 100644 index 000000000..1bac52d6d --- /dev/null +++ b/hilti/include/3rdparty/libtask/task.h @@ -0,0 +1,182 @@ +/* Copyright (c) 2005 Russ Cox, MIT; see COPYRIGHT */ + +#ifndef _TASK_H_ +#define _TASK_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* + * basic procs and threads + */ + +typedef struct Task Task; +typedef struct Tasklist Tasklist; + +int anyready(void); +int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); +void taskexit(int); +void taskexitall(int); +void taskmain(int argc, char *argv[]); +int taskyield(void); +void** taskdata(void); +void needstack(int); +void taskname(char*, ...); +void taskstate(char*, ...); +char* taskgetname(void); +char* taskgetstate(void); +void tasksystem(void); +unsigned int taskdelay(unsigned int); +unsigned int taskid(void); + +struct Tasklist /* used internally */ +{ + Task *head; + Task *tail; +}; + +/* + * queuing locks + */ +typedef struct QLock QLock; +struct QLock +{ + Task *owner; + Tasklist waiting; +}; + +void qlock(QLock*); +int canqlock(QLock*); +void qunlock(QLock*); + +/* + * reader-writer locks + */ +typedef struct RWLock RWLock; +struct RWLock +{ + int readers; + Task *writer; + Tasklist rwaiting; + Tasklist wwaiting; +}; + +void rlock(RWLock*); +int canrlock(RWLock*); +void runlock(RWLock*); + +void wlock(RWLock*); +int canwlock(RWLock*); +void wunlock(RWLock*); + +/* + * sleep and wakeup (condition variables) + */ +typedef struct Rendez Rendez; + +struct Rendez +{ + QLock *l; + Tasklist waiting; +}; + +void tasksleep(Rendez*); +int taskwakeup(Rendez*); +int taskwakeupall(Rendez*); + +/* + * channel communication + */ +typedef struct Alt Alt; +typedef struct Altarray Altarray; +typedef struct Channel Channel; + +enum +{ + CHANEND, + CHANSND, + CHANRCV, + CHANNOP, + CHANNOBLK, +}; + +struct Alt +{ + Channel *c; + void *v; + unsigned int op; + Task *task; + Alt *xalt; +}; + +struct Altarray +{ + Alt **a; + unsigned int n; + unsigned int m; +}; + +struct Channel +{ + unsigned int bufsize; + unsigned int elemsize; + unsigned char *buf; + unsigned int nbuf; + unsigned int off; + Altarray asend; + Altarray arecv; + char *name; +}; + +int chanalt(Alt *alts); +Channel* chancreate(int elemsize, int elemcnt); +void chanfree(Channel *c); +int chaninit(Channel *c, int elemsize, int elemcnt); +int channbrecv(Channel *c, void *v); +void* channbrecvp(Channel *c); +unsigned long channbrecvul(Channel *c); +int channbsend(Channel *c, void *v); +int channbsendp(Channel *c, void *v); +int channbsendul(Channel *c, unsigned long v); +int chanrecv(Channel *c, void *v); +void* chanrecvp(Channel *c); +unsigned long chanrecvul(Channel *c); +int chansend(Channel *c, void *v); +int chansendp(Channel *c, void *v); +int chansendul(Channel *c, unsigned long v); + +/* + * Threaded I/O. + */ +int fdread(int, void*, int); +int fdread1(int, void*, int); /* always uses fdwait */ +int fdwrite(int, void*, int); +void fdwait(int, int); +int fdnoblock(int); + +void fdtask(void*); + +/* + * Network dialing - sets non-blocking automatically + */ +enum +{ + UDP = 0, + TCP = 1, +}; + +int netannounce(int, char*, int); +int netaccept(int, char*, int*); +int netdial(int, char*, int); +int netlookup(char*, uint32_t*); /* blocks entire program! */ +int netdial(int, char*, int); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/hilti/include/3rdparty/libtask/taskimpl.h b/hilti/include/3rdparty/libtask/taskimpl.h new file mode 100644 index 000000000..0d5cb8113 --- /dev/null +++ b/hilti/include/3rdparty/libtask/taskimpl.h @@ -0,0 +1,191 @@ +/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ + +#if defined(__sun__) +# define __EXTENSIONS__ 1 /* SunOS */ +# if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__) + /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */ +# else +# define __MAKECONTEXT_V2_SOURCE 1 +# endif +#endif + +#define USE_UCONTEXT 1 + +#if defined(__OpenBSD__) || defined(__mips__) +#undef USE_UCONTEXT +#define USE_UCONTEXT 0 +#endif + +#if defined(__APPLE__) +#include +#if defined(MAC_OS_X_VERSION_10_5) +#undef USE_UCONTEXT +#define USE_UCONTEXT 0 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if USE_UCONTEXT +#include +#endif +#include +#include +#include + +#if 0 // HILTI +#include "task.h" + +#define nil ((void*)0) +#define nelem(x) (sizeof(x)/sizeof((x)[0])) + +#define ulong task_ulong +#define uint task_uint +#define uchar task_uchar +#define ushort task_ushort +#define uvlong task_uvlong +#define vlong task_vlong + +typedef unsigned long ulong; +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned long long uvlong; +typedef long long vlong; + +#define print task_print +#define fprint task_fprint +#define snprint task_snprint +#define seprint task_seprint +#define vprint task_vprint +#define vfprint task_vfprint +#define vsnprint task_vsnprint +#define vseprint task_vseprint +#define strecpy task_strecpy + +int print(char*, ...); +int fprint(int, char*, ...); +char *snprint(char*, uint, char*, ...); +char *seprint(char*, char*, char*, ...); +int vprint(char*, va_list); +int vfprint(int, char*, va_list); +char *vsnprint(char*, uint, char*, va_list); +char *vseprint(char*, char*, char*, va_list); +char *strecpy(char*, char*, char*); + +#if defined(__FreeBSD__) && __FreeBSD__ < 5 +extern int getmcontext(mcontext_t*); +extern void setmcontext(const mcontext_t*); +#define setcontext(u) setmcontext(&(u)->uc_mcontext) +#define getcontext(u) getmcontext(&(u)->uc_mcontext) +extern int swapcontext(ucontext_t*, const ucontext_t*); +extern void makecontext(ucontext_t*, void(*)(), int, ...); +#endif + +#endif // HILTI + +#if defined(__APPLE__) +# define mcontext libthread_mcontext +# define mcontext_t libthread_mcontext_t +# define ucontext libthread_ucontext +# define ucontext_t libthread_ucontext_t +# if defined(__i386__) +# include "386-ucontext.h" +# elif defined(__x86_64__) +# include "amd64-ucontext.h" +# else +# include "power-ucontext.h" +# endif +#endif + +#if defined(__OpenBSD__) +# define mcontext libthread_mcontext +# define mcontext_t libthread_mcontext_t +# define ucontext libthread_ucontext +# define ucontext_t libthread_ucontext_t +# if defined __i386__ +# include "386-ucontext.h" +# else +# include "power-ucontext.h" +# endif +extern pid_t rfork_thread(int, void*, int(*)(void*), void*); +#endif + +#if 0 && defined(__sun__) +# define mcontext libthread_mcontext +# define mcontext_t libthread_mcontext_t +# define ucontext libthread_ucontext +# define ucontext_t libthread_ucontext_t +# include "sparc-ucontext.h" +#endif + +#if defined(__arm__) +int getmcontext(mcontext_t*); +void setmcontext(const mcontext_t*); +#define setcontext(u) setmcontext(&(u)->uc_mcontext) +#define getcontext(u) getmcontext(&(u)->uc_mcontext) +#endif + +#if defined(__mips__) +#include "mips-ucontext.h" +int getmcontext(mcontext_t*); +void setmcontext(const mcontext_t*); +#define setcontext(u) setmcontext(&(u)->uc_mcontext) +#define getcontext(u) getmcontext(&(u)->uc_mcontext) +#endif + +#if 0 // HILTI + +typedef struct Context Context; + +enum +{ + STACK = 8192 +}; + +struct Context +{ + ucontext_t uc; +}; + +struct Task +{ + char name[256]; // offset known to acid + char state[256]; + Task *next; + Task *prev; + Task *allnext; + Task *allprev; + Context context; + uvlong alarmtime; + uint id; + uchar *stk; + uint stksize; + int exiting; + int alltaskslot; + int system; + int ready; + void (*startfn)(void*); + void *startarg; + void *udata; +}; + +void taskready(Task*); +void taskswitch(void); + +void addtask(Tasklist*, Task*); +void deltask(Tasklist*, Task*); + +extern Task *taskrunning; +extern int taskcount; + +#endif // HILTI diff --git a/hilti/include/3rdparty/nlohmann-json/LICENSE.MIT b/hilti/include/3rdparty/nlohmann-json/LICENSE.MIT new file mode 100644 index 000000000..8b0f7002e --- /dev/null +++ b/hilti/include/3rdparty/nlohmann-json/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2018 Niels Lohmann + +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/hilti/include/3rdparty/nlohmann-json/README.hilti b/hilti/include/3rdparty/nlohmann-json/README.hilti new file mode 100644 index 000000000..4627a8134 --- /dev/null +++ b/hilti/include/3rdparty/nlohmann-json/README.hilti @@ -0,0 +1 @@ +This is the JSON library from https://github.com/nlohmann/json. diff --git a/hilti/include/3rdparty/nlohmann-json/json.hpp b/hilti/include/3rdparty/nlohmann-json/json.hpp new file mode 100644 index 000000000..3dcb834be --- /dev/null +++ b/hilti/include/3rdparty/nlohmann-json/json.hpp @@ -0,0 +1,17190 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.1.1 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +Copyright (c) 2013-2018 Niels Lohmann . + +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. +*/ + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 1 +#define NLOHMANN_JSON_VERSION_PATCH 1 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // iterator_traits, random_access_iterator_tag +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap + +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; +} + +#endif + +// #include + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif +#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) +#else + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER +#endif + +// manual branch prediction +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) + #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + #define JSON_LIKELY(x) x + #define JSON_UNLIKELY(x) x +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +/*! +@brief Helper to determine whether there's a key_type for T. + +This helper is used to tell associative containers apart from other containers +such as sequence containers. For instance, `std::map` passes the test as it +contains a `mapped_type`, whereas `std::vector` fails the test. + +@sa http://stackoverflow.com/a/7728728/266378 +@since version 1.0.0, overworked in version 2.0.6 +*/ +#define NLOHMANN_JSON_HAS_HELPER(type) \ + template struct has_##type { \ + private: \ + template \ + static int detect(U &&); \ + static void detect(...); \ + public: \ + static constexpr bool value = \ + std::is_integral()))>::value; \ + } + +// #include + + +#include // not +#include // size_t +#include // numeric_limits +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // declval + +// #include + +// #include + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +/* +Implementation of two C++17 constructs: conjunction, negation. This is needed +to avoid evaluating all the traits in a condition + +For example: not std::is_same::value and has_value_type::value +will not compile when T = void (on MSVC at least). Whereas +conjunction>, has_value_type>::value will +stop evaluating if negation<...>::value == false + +Please note that those constructs must be used with caution, since symbols can +become very long quickly (which can slow down compilation and cause MSVC +internal compiler errors). Only use it when you have to (see example ahead). +*/ +template struct conjunction : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction : std::conditional, B1>::type {}; + +template struct negation : std::integral_constant {}; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +//////////////////////// +// has_/is_ functions // +//////////////////////// + +// source: https://stackoverflow.com/a/37193089/4116453 + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +NLOHMANN_JSON_HAS_HELPER(mapped_type); +NLOHMANN_JSON_HAS_HELPER(key_type); +NLOHMANN_JSON_HAS_HELPER(value_type); +NLOHMANN_JSON_HAS_HELPER(iterator); + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl +{ + static constexpr auto value = + std::is_constructible::value and + std::is_constructible::value; +}; + +template +struct is_compatible_object_type +{ + static auto constexpr value = is_compatible_object_type_impl < + conjunction>, + has_mapped_type, + has_key_type>::value, + typename BasicJsonType::object_t, CompatibleObjectType >::value; +}; + +template +struct is_basic_json_nested_type +{ + static auto constexpr value = std::is_same::value or + std::is_same::value or + std::is_same::value or + std::is_same::value; +}; + +template +struct is_compatible_array_type +{ + static auto constexpr value = + conjunction>, + negation>, + negation>, + negation>, + has_value_type, + has_iterator>::value; +}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + std::is_constructible::value and + CompatibleLimits::is_integer and + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type +{ + static constexpr auto value = + is_compatible_integer_type_impl < + std::is_integral::value and + not std::is_same::value, + RealIntegerType, CompatibleNumberIntegerType > ::value; +}; + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json +{ + private: + // also check the return type of from_json + template::from_json( + std::declval(), std::declval()))>::value>> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json +{ + private: + template < + typename U, + typename = enable_if_t::from_json(std::declval()))>::value >> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +template +struct has_to_json +{ + private: + template::to_json( + std::declval(), std::declval()))> + static int detect(U&&); + static void detect(...); + + public: + static constexpr bool value = std::is_integral>()))>::value; +}; + +template +struct is_compatible_complete_type +{ + static constexpr bool value = + not std::is_base_of::value and + not std::is_same::value and + not is_basic_json_nested_type::value and + has_to_json::value; +}; + +template +struct is_compatible_type + : conjunction, + is_compatible_complete_type> +{ +}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} +} + +// #include + + +#include // exception +#include // runtime_error +#include // to_string + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa @ref exception for the base class of the library exceptions +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] byte_ the byte index where the error occurred (or 0 if the + position cannot be determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref out_of_range for exceptions indicating access out of the defined range +@sa @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa @ref exception for the base class of the library exceptions +@sa @ref parse_error for exceptions indicating a parse error +@sa @ref invalid_iterator for exceptions indicating errors with iterators +@sa @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} +} + +// #include + + +#include // array +#include // and +#include // size_t +#include // uint8_t + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + discarded ///< discarded by the the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string +- furthermore, each type is not smaller than itself +- discarded values are not comparable + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; +} +} +} + +// #include + + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // pair, declval +#include // valarray + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// overloads for basic_json template parameters +template::value and + not std::is_same::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_UNLIKELY(not j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr) +{ + if (JSON_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + arr = *j.template get_ptr(); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); +} + +template +void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/) +{ + using std::end; + + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); +} + +template +auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + void()) +{ + using std::end; + + arr.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(arr, end(arr)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); +} + +template +void from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template < + typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < + is_compatible_array_type::value and + not std::is_same::value and + std::is_constructible < + BasicJsonType, typename CompatibleArrayType::value_type >::value, + int > = 0 > +void from_json(const BasicJsonType& j, CompatibleArrayType& arr) +{ + if (JSON_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<2> {}); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, CompatibleObjectType& obj) +{ + if (JSON_UNLIKELY(not j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + auto inner_object = j.template get_ptr(); + using value_type = typename CompatibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(obj, obj.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} + +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} + +struct from_json_fn +{ + private: + template + auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } + + template + void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept + { + static_assert(sizeof(BasicJsonType) == 0, + "could not find from_json() method in T's namespace"); +#ifdef _MSC_VER + // MSVC does not show a stacktrace for the above assert + using decayed = uncvref_t; + static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, + "forcing MSVC stacktrace to show which T we're talking about."); +#endif + } + + public: + template + void operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1> {}))) + { + return call(j, val, priority_tag<1> {}); + } +}; +} + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} +} + +// #include + + +#include // or, and, not +#include // begin, end +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template::value or + std::is_same::value, + int> = 0> +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, std::valarray arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, T (&arr)[N]) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = {p.first, p.second}; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence) +{ + j = {std::get(t)...}; +} + +template +void to_json(BasicJsonType& j, const std::tuple& t) +{ + to_json_tuple_impl(j, t, index_sequence_for {}); +} + +struct to_json_fn +{ + private: + template + auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } + + template + void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept + { + static_assert(sizeof(BasicJsonType) == 0, + "could not find to_json() method in T's namespace"); + +#ifdef _MSC_VER + // MSVC does not show a stacktrace for the above assert + using decayed = uncvref_t; + static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0, + "forcing MSVC stacktrace to show which T we're talking about."); +#endif + } + + public: + template + void operator()(BasicJsonType& j, T&& val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1> {}))) + { + return call(j, std::forward(val), priority_tag<1> {}); + } +}; +} + +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} +} + +// #include + + +#include // min +#include // array +#include // assert +#include // size_t +#include // strlen +#include // streamsize, streamoff, streampos +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////////////// +// input adapters // +//////////////////// + +/*! +@brief abstract input adapter interface + +Produces a stream of std::char_traits::int_type characters from a +std::istream, a buffer, or some other input type. Accepts the return of exactly +one non-EOF character for future input. The int_type characters returned +consist of all valid char values as positive values (typically unsigned char), +plus an EOF value outside that range, specified by the value of the function +std::char_traits::eof(). This value is typically -1, but could be any +arbitrary value which is not a valid char value. +*/ +struct input_adapter_protocol +{ + /// get a character [0,255] or std::char_traits::eof(). + virtual std::char_traits::int_type get_character() = 0; + /// restore the last non-eof() character to input + virtual void unget_character() = 0; + virtual ~input_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +using input_adapter_t = std::shared_ptr; + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter : public input_adapter_protocol +{ + public: + ~input_stream_adapter() override + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags + is.clear(); + } + + explicit input_stream_adapter(std::istream& i) + : is(i), sb(*i.rdbuf()) + { + // skip byte order mark + std::char_traits::int_type c; + if ((c = get_character()) == 0xEF) + { + if ((c = get_character()) == 0xBB) + { + if ((c = get_character()) == 0xBF) + { + return; // Ignore BOM + } + else if (c != std::char_traits::eof()) + { + is.unget(); + } + is.putback('\xBB'); + } + else if (c != std::char_traits::eof()) + { + is.unget(); + } + is.putback('\xEF'); + } + else if (c != std::char_traits::eof()) + { + is.unget(); // no byte order mark; process as usual + } + } + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() override + { + return sb.sbumpc(); + } + + void unget_character() override + { + sb.sungetc(); // is.unget() avoided for performance + } + + private: + /// the associated input stream + std::istream& is; + std::streambuf& sb; +}; + +/// input adapter for buffer input +class input_buffer_adapter : public input_adapter_protocol +{ + public: + input_buffer_adapter(const char* b, const std::size_t l) + : cursor(b), limit(b + l), start(b) + { + // skip byte order mark + if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF') + { + cursor += 3; + } + } + + // delete because of pointer members + input_buffer_adapter(const input_buffer_adapter&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + + std::char_traits::int_type get_character() noexcept override + { + if (JSON_LIKELY(cursor < limit)) + { + return std::char_traits::to_int_type(*(cursor++)); + } + + return std::char_traits::eof(); + } + + void unget_character() noexcept override + { + if (JSON_LIKELY(cursor > start)) + { + --cursor; + } + } + + private: + /// pointer to the current character + const char* cursor; + /// pointer past the last character + const char* limit; + /// pointer to the first character + const char* start; +}; + +class input_adapter +{ + public: + // native support + + /// input adapter for input stream + input_adapter(std::istream& i) + : ia(std::make_shared(i)) {} + + /// input adapter for input stream + input_adapter(std::istream&& i) + : ia(std::make_shared(i)) {} + + /// input adapter for buffer + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + // derived support + + /// input adapter for string literal + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + /// input adapter for iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + input_adapter(IteratorType first, IteratorType last) + { + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + assert(std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first); + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename std::iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + ia = std::make_shared(nullptr, len); + } + } + + /// input adapter for array + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + /// input adapter for contiguous container + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() + { + return ia; + } + + private: + /// the actual adapter + input_adapter_t ia = nullptr; +}; +} +} + +// #include + + +#include // localeconv +#include // size_t +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // hex, uppercase +#include // setw, setfill +#include // stringstream +#include // char_traits, string +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + default: // catch non-enum values + return "unknown token"; // LCOV_EXCL_LINE + } + } + + explicit lexer(detail::input_adapter_t adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer& operator=(lexer&) = delete; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + static char get_decimal_point() noexcept + { + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + assert(current == 'u'); + int codepoint = 0; + + const auto factors = { 12, 8, 4, 0 }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' and current <= '9') + { + codepoint += ((current - 0x30) << factor); + } + else if (current >= 'A' and current <= 'F') + { + codepoint += ((current - 0x37) << factor); + } + else if (current >= 'a' and current <= 'f') + { + codepoint += ((current - 0x57) << factor); + } + else + { + return -1; + } + } + + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_LIKELY(*range <= current and current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + assert(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_LIKELY(get() == '\\' and get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = + // high surrogate occupies the most significant 22 bits + (codepoint1 << 10) + // low surrogate occupies the least significant 15 bits + + codepoint2 + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00; + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(codepoint); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(0xC0 | (codepoint >> 6)); + add(0x80 | (codepoint & 0x3F)); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(0xE0 | (codepoint >> 12)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(0xF0 | (codepoint >> 18)); + add(0x80 | ((codepoint >> 12) & 0x3F)); + add(0x80 | ((codepoint >> 6) & 0x3F)); + add(0x80 | (codepoint & 0x3F)); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + { + error_message = "invalid string: control character must be escaped"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + // all other characters are rejected outside scan_number() + assert(false); // LCOV_EXCL_LINE + } + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + token_type scan_literal(const char* literal_text, const std::size_t length, + token_type return_type) + { + assert(current == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_UNLIKELY(get() != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + std::char_traits::int_type get() + { + ++chars_read; + current = ia->get_character(); + if (JSON_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + return current; + } + + /// unget current character (return it again on next get) + void unget() + { + --chars_read; + if (JSON_LIKELY(current != std::char_traits::eof())) + { + ia->unget_character(); + assert(token_string.size() != 0); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(int c) + { + token_buffer.push_back(std::char_traits::to_char_type(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + std::string move_string() + { + return std::move(token_buffer); + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr std::size_t get_position() const noexcept + { + return chars_read; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if ('\x00' <= c and c <= '\x1F') + { + // escape control characters + std::stringstream ss; + ss << "(c) << ">"; + result += ss.str(); + } + else + { + // add character as is + result.push_back(c); + } + } + + return result; + } + + /// return syntax error message + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + token_type scan() + { + // read next character and ignore whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + detail::input_adapter_t ia = nullptr; + + /// the current character + std::char_traits::int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + std::string token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char decimal_point_char = '.'; +}; +} +} + +// #include + + +#include // assert +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +/*! +@brief syntax analysis + +This class implements a recursive decent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + using parser_callback_t = + std::function; + + /// a parser reading from an input adapter + explicit parser(detail::input_adapter_t adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_) + {} + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + // read first token + get_token(); + + parse_internal(true, result); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict) + { + get_token(); + expect(token_type::end_of_input); + } + + // in case of an error, return discarded value + if (errored) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + // read first token + get_token(); + + if (not accept_internal()) + { + return false; + } + + // strict => last token must be EOF + return not strict or (get_token() == token_type::end_of_input); + } + + private: + /*! + @brief the actual parser + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse_internal(bool keep, BasicJsonType& result) + { + // never parse after a parse error was detected + assert(not errored); + + // start with a discarded value + if (not result.is_discarded()) + { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + + switch (last_token) + { + case token_type::begin_object: + { + if (keep) + { + if (callback) + { + keep = callback(depth++, parse_event_t::object_start, result); + } + + if (not callback or keep) + { + // explicitly set result to object to cope with {} + result.m_type = value_t::object; + result.m_value = value_t::object; + } + } + + // read next token + get_token(); + + // closing } -> we are done + if (last_token == token_type::end_object) + { + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + // parse values + std::string key; + BasicJsonType value; + while (true) + { + // store key + if (not expect(token_type::value_string)) + { + return; + } + key = m_lexer.move_string(); + + bool keep_tag = false; + if (keep) + { + if (callback) + { + BasicJsonType k(key); + keep_tag = callback(depth, parse_event_t::key, k); + } + else + { + keep_tag = true; + } + } + + // parse separator (:) + get_token(); + if (not expect(token_type::name_separator)) + { + return; + } + + // parse and add value + get_token(); + value.m_value.destroy(value.m_type); + value.m_type = value_t::discarded; + parse_internal(keep, value); + + if (JSON_UNLIKELY(errored)) + { + return; + } + + if (keep and keep_tag and not value.is_discarded()) + { + result.m_value.object->emplace(std::move(key), std::move(value)); + } + + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } + + // closing } + if (not expect(token_type::end_object)) + { + return; + } + break; + } + + if (keep and callback and not callback(--depth, parse_event_t::object_end, result)) + { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + case token_type::begin_array: + { + if (keep) + { + if (callback) + { + keep = callback(depth++, parse_event_t::array_start, result); + } + + if (not callback or keep) + { + // explicitly set result to array to cope with [] + result.m_type = value_t::array; + result.m_value = value_t::array; + } + } + + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == token_type::end_array) + { + if (callback and not callback(--depth, parse_event_t::array_end, result)) + { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + // parse values + BasicJsonType value; + while (true) + { + // parse value + value.m_value.destroy(value.m_type); + value.m_type = value_t::discarded; + parse_internal(keep, value); + + if (JSON_UNLIKELY(errored)) + { + return; + } + + if (keep and not value.is_discarded()) + { + result.m_value.array->push_back(std::move(value)); + } + + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } + + // closing ] + if (not expect(token_type::end_array)) + { + return; + } + break; + } + + if (keep and callback and not callback(--depth, parse_event_t::array_end, result)) + { + result.m_value.destroy(result.m_type); + result.m_type = value_t::discarded; + } + break; + } + + case token_type::literal_null: + { + result.m_type = value_t::null; + break; + } + + case token_type::value_string: + { + result.m_type = value_t::string; + result.m_value = m_lexer.move_string(); + break; + } + + case token_type::literal_true: + { + result.m_type = value_t::boolean; + result.m_value = true; + break; + } + + case token_type::literal_false: + { + result.m_type = value_t::boolean; + result.m_value = false; + break; + } + + case token_type::value_unsigned: + { + result.m_type = value_t::number_unsigned; + result.m_value = m_lexer.get_number_unsigned(); + break; + } + + case token_type::value_integer: + { + result.m_type = value_t::number_integer; + result.m_value = m_lexer.get_number_integer(); + break; + } + + case token_type::value_float: + { + result.m_type = value_t::number_float; + result.m_value = m_lexer.get_number_float(); + + // throw in case of infinity or NAN + if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float))) + { + if (allow_exceptions) + { + JSON_THROW(out_of_range::create(406, "number overflow parsing '" + + m_lexer.get_token_string() + "'")); + } + expect(token_type::uninitialized); + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + if (not expect(token_type::uninitialized)) + { + return; + } + break; // LCOV_EXCL_LINE + } + + default: + { + // the last token was unexpected; we expected a value + if (not expect(token_type::literal_or_value)) + { + return; + } + break; // LCOV_EXCL_LINE + } + } + + if (keep and callback and not callback(depth, parse_event_t::value, result)) + { + result.m_type = value_t::discarded; + } + } + + /*! + @brief the actual acceptor + + @invariant 1. The last token is not yet processed. Therefore, the caller + of this function must make sure a token has been read. + 2. When this function returns, the last token is processed. + That is, the last read character was already considered. + + This invariant makes sure that no token needs to be "unput". + */ + bool accept_internal() + { + switch (last_token) + { + case token_type::begin_object: + { + // read next token + get_token(); + + // closing } -> we are done + if (last_token == token_type::end_object) + { + return true; + } + + // parse values + while (true) + { + // parse key + if (last_token != token_type::value_string) + { + return false; + } + + // parse separator (:) + get_token(); + if (last_token != token_type::name_separator) + { + return false; + } + + // parse value + get_token(); + if (not accept_internal()) + { + return false; + } + + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } + + // closing } + return (last_token == token_type::end_object); + } + } + + case token_type::begin_array: + { + // read next token + get_token(); + + // closing ] -> we are done + if (last_token == token_type::end_array) + { + return true; + } + + // parse values + while (true) + { + // parse value + if (not accept_internal()) + { + return false; + } + + // comma -> next value + get_token(); + if (last_token == token_type::value_separator) + { + get_token(); + continue; + } + + // closing ] + return (last_token == token_type::end_array); + } + } + + case token_type::value_float: + { + // reject infinity or NAN + return std::isfinite(m_lexer.get_number_float()); + } + + case token_type::literal_false: + case token_type::literal_null: + case token_type::literal_true: + case token_type::value_integer: + case token_type::value_string: + case token_type::value_unsigned: + return true; + + default: // the last token was unexpected + return false; + } + } + + /// get next token from lexer + token_type get_token() + { + return (last_token = m_lexer.scan()); + } + + /*! + @throw parse_error.101 if expected token did not occur + */ + bool expect(token_type t) + { + if (JSON_UNLIKELY(t != last_token)) + { + errored = true; + expected = t; + if (allow_exceptions) + { + throw_exception(); + } + else + { + return false; + } + } + + return true; + } + + [[noreturn]] void throw_exception() const + { + std::string error_msg = "syntax error - "; + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg)); + } + + private: + /// current level of recursion + int depth = 0; + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether a syntax error occurred + bool errored = false; + /// possible reason for the syntax error + token_type expected = token_type::uninitialized; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} +} + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + m_it++; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept + { + auto result = *this; + m_it--; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} +} + +// #include + + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} +} + +// #include + + +#include // not +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class + +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. + +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). + +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) {} + + /*! + @brief converting assignment + @param[in,out] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + typename object_t::key_type key() const + { + assert(m_object != nullptr); + + if (JSON_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it; +}; +} +} + +// #include + + +#include // size_t +#include // string, to_string + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// helper class for iteration + class iteration_proxy_internal + { + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + + public: + explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {} + + /// dereference operator (needed for range-based for) + iteration_proxy_internal& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_internal& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_internal& o) const noexcept + { + return anchor != o.anchor; + } + + /// return key of the iterator + std::string key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + return std::to_string(array_index); + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return ""; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } + }; + + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_internal begin() noexcept + { + return iteration_proxy_internal(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_internal end() noexcept + { + return iteration_proxy_internal(container.end()); + } +}; +} +} + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} +} + +// #include + + +#include // copy +#include // size_t +#include // streamsize +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_ostream +#include // basic_string +#include // vector + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) : v(vec) {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) : stream(s) {} + + void write_character(CharType c) override + { + stream.put(c); + } + + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; + +/// output adapter for basic_string +template +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(std::basic_string& s) : str(s) {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + std::basic_string& str; +}; + +template +class output_adapter +{ + public: + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} + + output_adapter(std::basic_string& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} +} + +// #include + + +#include // generate_n +#include // array +#include // assert +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // setw, setfill +#include // hex +#include // back_inserter +#include // numeric_limits +#include // stringstream +#include // char_traits, string +#include // make_pair, move + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR and MessagePack values +*/ +template +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using string_t = typename BasicJsonType::string_t; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + { + assert(ia); + } + + /*! + @brief create a JSON value from CBOR input + + @param[in] strict whether to expect the input to be consumed completed + @return JSON value created from CBOR input + + @throw parse_error.110 if input ended unexpectedly or the end of file was + not reached when @a strict was set to true + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_cbor(const bool strict) + { + const auto res = parse_cbor_internal(); + if (strict) + { + get(); + expect_eof(); + } + return res; + } + + /*! + @brief create a JSON value from MessagePack input + + @param[in] strict whether to expect the input to be consumed completed + @return JSON value created from MessagePack input + + @throw parse_error.110 if input ended unexpectedly or the end of file was + not reached when @a strict was set to true + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_msgpack(const bool strict) + { + const auto res = parse_msgpack_internal(); + if (strict) + { + get(); + expect_eof(); + } + return res; + } + + /*! + @brief create a JSON value from UBJSON input + + @param[in] strict whether to expect the input to be consumed completed + @return JSON value created from UBJSON input + + @throw parse_error.110 if input ended unexpectedly or the end of file was + not reached when @a strict was set to true + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_ubjson(const bool strict) + { + const auto res = parse_ubjson_internal(); + if (strict) + { + get_ignore_noop(); + expect_eof(); + } + return res; + } + + /*! + @brief determine system byte order + + @return true if and only if system's byte order is little endian + + @note from http://stackoverflow.com/a/1001328/266378 + */ + static constexpr bool little_endianess(int num = 1) noexcept + { + return (*reinterpret_cast(&num) == 1); + } + + private: + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + */ + BasicJsonType parse_cbor_internal(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return static_cast(current); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + return get_number(); + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + return get_number(); + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + return get_number(); + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + return get_number(); + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return static_cast(0x20 - 1 - current); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + return static_cast(-1) - + static_cast(get_number()); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + return get_cbor_string(); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + return get_cbor_array(current & 0x1F); + } + + case 0x98: // array (one-byte uint8_t for n follows) + { + return get_cbor_array(get_number()); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + return get_cbor_array(get_number()); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + return get_cbor_array(get_number()); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + return get_cbor_array(get_number()); + } + + case 0x9F: // array (indefinite length) + { + BasicJsonType result = value_t::array; + while (get() != 0xFF) + { + result.push_back(parse_cbor_internal(false)); + } + return result; + } + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + { + return get_cbor_object(current & 0x1F); + } + + case 0xB8: // map (one-byte uint8_t for n follows) + { + return get_cbor_object(get_number()); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + return get_cbor_object(get_number()); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + return get_cbor_object(get_number()); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + return get_cbor_object(get_number()); + } + + case 0xBF: // map (indefinite length) + { + BasicJsonType result = value_t::object; + while (get() != 0xFF) + { + auto key = get_cbor_string(); + result[key] = parse_cbor_internal(); + } + return result; + } + + case 0xF4: // false + { + return false; + } + + case 0xF5: // true + { + return true; + } + + case 0xF6: // null + { + return value_t::null; + } + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1 = get(); + unexpect_eof(); + const int byte2 = get(); + unexpect_eof(); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const int half = (byte1 << 8) + byte2; + const int exp = (half >> 10) & 0x1F; + const int mant = half & 0x3FF; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = (mant == 0) ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + } + return (half & 0x8000) != 0 ? -val : val; + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + return get_number(); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + return get_number(); + } + + default: // anything else (0xFF is handled inside the other types) + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); + } + } + } + + BasicJsonType parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return static_cast(current); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + { + return get_msgpack_object(current & 0x0F); + } + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + { + return get_msgpack_array(current & 0x0F); + } + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + return get_msgpack_string(); + + case 0xC0: // nil + return value_t::null; + + case 0xC2: // false + return false; + + case 0xC3: // true + return true; + + case 0xCA: // float 32 + return get_number(); + + case 0xCB: // float 64 + return get_number(); + + case 0xCC: // uint 8 + return get_number(); + + case 0xCD: // uint 16 + return get_number(); + + case 0xCE: // uint 32 + return get_number(); + + case 0xCF: // uint 64 + return get_number(); + + case 0xD0: // int 8 + return get_number(); + + case 0xD1: // int 16 + return get_number(); + + case 0xD2: // int 32 + return get_number(); + + case 0xD3: // int 64 + return get_number(); + + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + return get_msgpack_string(); + + case 0xDC: // array 16 + { + return get_msgpack_array(get_number()); + } + + case 0xDD: // array 32 + { + return get_msgpack_array(get_number()); + } + + case 0xDE: // map 16 + { + return get_msgpack_object(get_number()); + } + + case 0xDF: // map 32 + { + return get_msgpack_object(get_number()); + } + + // positive fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return static_cast(current); + + default: // anything else + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(112, chars_read, + "error reading MessagePack; last byte: 0x" + ss.str())); + } + } + } + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + */ + BasicJsonType parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + int get() + { + ++chars_read; + return (current = ia->get_character()); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + int get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + + @return number of type @a NumberType + + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. + + @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes + */ + template NumberType get_number() + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + unexpect_eof(); + + // reverse byte order prior to conversion if necessary + if (is_little_endian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + NumberType result; + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return result; + } + + /*! + @brief create a string by reading characters from the input + + @param[in] len number of bytes to read + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + + @return string created by reading @a len bytes + + @throw parse_error.110 if input has less than @a len bytes + */ + template + string_t get_string(const NumberType len) + { + string_t result; + std::generate_n(std::back_inserter(result), len, [this]() + { + get(); + unexpect_eof(); + return static_cast(current); + }); + return result; + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @return string + + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + string_t get_cbor_string() + { + unexpect_eof(); + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(current & 0x1F); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + return get_string(get_number()); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + return get_string(get_number()); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + return get_string(get_number()); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + return get_string(get_number()); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + string_t result; + while (get() != 0xFF) + { + result.append(get_cbor_string()); + } + return result; + } + + default: + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); + } + } + } + + template + BasicJsonType get_cbor_array(const NumberType len) + { + BasicJsonType result = value_t::array; + std::generate_n(std::back_inserter(*result.m_value.array), len, [this]() + { + return parse_cbor_internal(); + }); + return result; + } + + template + BasicJsonType get_cbor_object(const NumberType len) + { + BasicJsonType result = value_t::object; + std::generate_n(std::inserter(*result.m_value.object, + result.m_value.object->end()), + len, [this]() + { + get(); + auto key = get_cbor_string(); + auto val = parse_cbor_internal(); + return std::make_pair(std::move(key), std::move(val)); + }); + return result; + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @return string + + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + string_t get_msgpack_string() + { + unexpect_eof(); + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(current & 0x1F); + } + + case 0xD9: // str 8 + { + return get_string(get_number()); + } + + case 0xDA: // str 16 + { + return get_string(get_number()); + } + + case 0xDB: // str 32 + { + return get_string(get_number()); + } + + default: + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(113, chars_read, + "expected a MessagePack string; last byte: 0x" + ss.str())); + } + } + } + + template + BasicJsonType get_msgpack_array(const NumberType len) + { + BasicJsonType result = value_t::array; + std::generate_n(std::back_inserter(*result.m_value.array), len, [this]() + { + return parse_msgpack_internal(); + }); + return result; + } + + template + BasicJsonType get_msgpack_object(const NumberType len) + { + BasicJsonType result = value_t::object; + std::generate_n(std::inserter(*result.m_value.object, + result.m_value.object->end()), + len, [this]() + { + get(); + auto key = get_msgpack_string(); + auto val = parse_msgpack_internal(); + return std::make_pair(std::move(key), std::move(val)); + }); + return result; + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return string + + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + string_t get_ubjson_string(const bool get_char = true) + { + if (get_char) + { + get(); // TODO: may we ignore N here? + } + + unexpect_eof(); + + switch (current) + { + case 'U': + return get_string(get_number()); + case 'i': + return get_string(get_number()); + case 'I': + return get_string(get_number()); + case 'l': + return get_string(get_number()); + case 'L': + return get_string(get_number()); + default: + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(113, chars_read, + "expected a UBJSON string; last byte: 0x" + ss.str())); + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @return pair of the size and the type + */ + std::pair get_ubjson_size_type() + { + std::size_t sz = string_t::npos; + int tc = 0; + + get_ignore_noop(); + + if (current == '$') + { + tc = get(); // must not ignore 'N', because 'N' maybe the type + unexpect_eof(); + + get_ignore_noop(); + if (current != '#') + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(112, chars_read, + "expected '#' after UBJSON type information; last byte: 0x" + ss.str())); + } + sz = parse_ubjson_internal(); + } + else if (current == '#') + { + sz = parse_ubjson_internal(); + } + + return std::make_pair(sz, tc); + } + + BasicJsonType get_ubjson_value(const int prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + + case 'T': // true + return true; + case 'F': // false + return false; + + case 'Z': // null + return nullptr; + + case 'U': + return get_number(); + case 'i': + return get_number(); + case 'I': + return get_number(); + case 'l': + return get_number(); + case 'L': + return get_number(); + case 'd': + return get_number(); + case 'D': + return get_number(); + + case 'C': // char + { + get(); + unexpect_eof(); + if (JSON_UNLIKELY(current > 127)) + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(113, chars_read, + "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + ss.str())); + } + return string_t(1, static_cast(current)); + } + + case 'S': // string + return get_ubjson_string(); + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create(112, chars_read, + "error reading UBJSON; last byte: 0x" + ss.str())); + } + } + + BasicJsonType get_ubjson_array() + { + BasicJsonType result = value_t::array; + const auto size_and_type = get_ubjson_size_type(); + + if (size_and_type.first != string_t::npos) + { + if (JSON_UNLIKELY(size_and_type.first > result.max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(size_and_type.first))); + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + std::generate_n(std::back_inserter(*result.m_value.array), + size_and_type.first, [this, size_and_type]() + { + return get_ubjson_value(size_and_type.second); + }); + } + } + else + { + std::generate_n(std::back_inserter(*result.m_value.array), + size_and_type.first, [this]() + { + return parse_ubjson_internal(); + }); + } + } + else + { + while (current != ']') + { + result.push_back(parse_ubjson_internal(false)); + get_ignore_noop(); + } + } + + return result; + } + + BasicJsonType get_ubjson_object() + { + BasicJsonType result = value_t::object; + const auto size_and_type = get_ubjson_size_type(); + + if (size_and_type.first != string_t::npos) + { + if (JSON_UNLIKELY(size_and_type.first > result.max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(size_and_type.first))); + } + + if (size_and_type.second != 0) + { + std::generate_n(std::inserter(*result.m_value.object, + result.m_value.object->end()), + size_and_type.first, [this, size_and_type]() + { + auto key = get_ubjson_string(); + auto val = get_ubjson_value(size_and_type.second); + return std::make_pair(std::move(key), std::move(val)); + }); + } + else + { + std::generate_n(std::inserter(*result.m_value.object, + result.m_value.object->end()), + size_and_type.first, [this]() + { + auto key = get_ubjson_string(); + auto val = parse_ubjson_internal(); + return std::make_pair(std::move(key), std::move(val)); + }); + } + } + else + { + while (current != '}') + { + auto key = get_ubjson_string(false); + result[std::move(key)] = parse_ubjson_internal(); + get_ignore_noop(); + } + } + + return result; + } + + /*! + @brief throw if end of input is not reached + @throw parse_error.110 if input not ended + */ + void expect_eof() const + { + if (JSON_UNLIKELY(current != std::char_traits::eof())) + { + JSON_THROW(parse_error::create(110, chars_read, "expected end of input")); + } + } + + /*! + @briefthrow if end of input is reached + @throw parse_error.110 if input ended + */ + void unexpect_eof() const + { + if (JSON_UNLIKELY(current == std::char_traits::eof())) + { + JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + } + } + + private: + /// input adapter + input_adapter_t ia = nullptr; + + /// the current character + int current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); +}; +} +} + +// #include + + +#include // reverse +#include // array +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(adapter) + { + assert(oa); + } + + /*! + @brief[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(static_cast(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? static_cast(0xF5) + : static_cast(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(static_cast(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(static_cast(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(static_cast(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: // Double-Precision Float + { + oa->write_character(static_cast(0xFB)); + write_number(j.m_value.number_float); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + default: + break; + } + } + + /*! + @brief[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(static_cast(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? static_cast(0xC3) + : static_cast(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(static_cast(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(static_cast(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(static_cast(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(static_cast(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(static_cast(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(static_cast(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(static_cast(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() and + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(static_cast(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(static_cast(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(static_cast(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(static_cast(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(static_cast(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: // float 64 + { + oa->write_character(static_cast(0xCB)); + write_number(j.m_value.number_float); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(static_cast(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(static_cast(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(static_cast(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(static_cast(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(static_cast(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(static_cast(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(static_cast(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(static_cast('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + oa->write_character(j.m_value.boolean + ? static_cast('T') + : static_cast('F')); + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(static_cast('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(static_cast('[')); + } + + bool prefix_required = true; + if (use_type and not j.m_value.array->empty()) + { + assert(use_count); + const char first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(static_cast('$')); + oa->write_character(static_cast(first_prefix)); + } + } + + if (use_count) + { + oa->write_character(static_cast('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (not use_count) + { + oa->write_character(static_cast(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(static_cast('{')); + } + + bool prefix_required = true; + if (use_type and not j.m_value.object->empty()) + { + assert(use_count); + const char first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(static_cast('$')); + oa->write_character(static_cast(first_prefix)); + } + } + + if (use_count) + { + oa->write_character(static_cast('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (not use_count) + { + oa->write_character(static_cast('}')); + } + + break; + } + + default: + break; + } + } + + private: + /* + @brief write a number to output input + + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + + @note This function needs to respect the system's endianess, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + template + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (std::is_floating_point::value) + { + if (add_prefix) + { + oa->write_character(static_cast('D')); // float64 + } + write_number(n); + } + else if (std::is_unsigned::value) + { + if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + } + } + else + { + if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('i')); // int8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(static_cast('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + JSON_THROW(out_of_range::create(407, "number overflow serializing " + std::to_string(n))); + } + // LCOV_EXCL_STOP + } + } + + /*! + @brief determine the type prefix of container values + + @note This function does not need to be 100% accurate when it comes to + integer limits. In case a number exceeds the limits of int64_t, + this will be detected by a later call to function + write_number_with_ubjson_prefix. Therefore, we return 'L' for any + value that does not fit the previous limits. + */ + char ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + else if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + else if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + else if ((std::numeric_limits::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + else // no check and assume int64_t (see note above) + { + return 'L'; + } + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'i'; + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'U'; + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'I'; + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + return 'l'; + } + else // no check and assume int64_t (see note above) + { + return 'L'; + } + } + + case value_t::number_float: + return 'D'; + + case value_t::string: + return 'S'; + + case value_t::array: + return '['; + + case value_t::object: + return '{'; + + default: // discarded values + return 'N'; + } + } + + private: + /// whether we can assume little endianess + const bool is_little_endian = binary_reader::little_endianess(); + + /// the output + output_adapter_t oa = nullptr; +}; +} +} + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // assert +#include // and, or +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // setfill +#include // next +#include // numeric_limits +#include // string +#include // stringstream +#include // is_same + +// #include + +// #include + + +#include // assert +#include // or, and, not +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + uint64_t f; + int e; + + constexpr diyfp() noexcept : f(0), e(0) {} + constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + assert(x.e == y.e); + assert(x.f >= y.f); + + return diyfp(x.f - y.f, x.e); + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const uint64_t u_lo = x.f & 0xFFFFFFFF; + const uint64_t u_hi = x.f >> 32; + const uint64_t v_lo = y.f & 0xFFFFFFFF; + const uint64_t v_hi = y.f >> 32; + + const uint64_t p0 = u_lo * v_lo; + const uint64_t p1 = u_lo * v_hi; + const uint64_t p2 = u_hi * v_lo; + const uint64_t p3 = u_hi * v_hi; + + const uint64_t p0_hi = p0 >> 32; + const uint64_t p1_lo = p1 & 0xFFFFFFFF; + const uint64_t p1_hi = p1 >> 32; + const uint64_t p2_lo = p2 & 0xFFFFFFFF; + const uint64_t p2_hi = p2 >> 32; + + uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += uint64_t{1} << (64 - 32 - 1); // round, ties up + + const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32); + + return diyfp(h, x.e + y.e + 64); + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + assert(x.f != 0); + + while ((x.f >> 63) == 0) + { + x.f <<= 1; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + assert(delta >= 0); + assert(((x.f << delta) >> delta) == x.f); + + return diyfp(x.f << delta, target_exponent); + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + assert(std::isfinite(value)); + assert(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type; + + const uint64_t bits = reinterpret_bits(value); + const uint64_t E = bits >> (kPrecision - 1); + const uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = (E == 0); + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = (F == 0 and E > 1); + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersSize = 79; + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr cached_power kCachedPowers[] = + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + assert(e >= -1500); + assert(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + (f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + assert(index >= 0); + assert(index < kCachedPowersSize); + static_cast(kCachedPowersSize); // Fix warning. + + const cached_power cached = kCachedPowers[index]; + assert(kAlpha <= cached.e + e + 64); + assert(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const uint32_t n, uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta, + uint64_t rest, uint64_t ten_k) +{ + assert(len >= 1); + assert(dist <= delta); + assert(rest <= delta); + assert(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + and delta - rest >= ten_k + and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + { + assert(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + assert(M_plus.e >= kAlpha); + assert(M_plus.e <= kGamma); + + uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e); + + uint32_t p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + assert(p1 > 0); + + uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const uint64_t rest = (uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const uint64_t ten_n = uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + assert(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + assert(p2 <= UINT64_MAX / 10); + p2 *= 10; + const uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + assert(m_plus.e == m_minus.e); + assert(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + assert(std::isfinite(value)); + assert(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (http://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +inline char* append_exponent(char* buf, int e) +{ + assert(e > -1000); + assert(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + uint32_t k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + assert(min_exp < 0); + assert(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n and n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n - k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (n + 2); + } + + if (0 < n and n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + assert(k > n); + + std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + buf[n] = '.'; + return buf + (k + 1); + } + + if (min_exp < n and n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + -n), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2 + (-n) + k); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + buf[1] = '.'; + buf += 1 + k; + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +char* to_chars(char* first, char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + assert(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + assert(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + assert(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + assert(last - first >= kMaxExp + 2); + assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + assert(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + static constexpr uint8_t UTF8_ACCEPT = 0; + static constexpr uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + */ + serializer(output_adapter_t s, const char ichar) + : o(std::move(s)), loc(std::localeconv()), + thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)), + decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)), + indent_char(ichar), indent_string(512, indent_char) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + assert(i != val.m_value.object->cend()); + assert(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + assert(i != val.m_value.object->cend()); + assert(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + assert(not val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + assert(not val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + } + } + + private: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + uint32_t codepoint; + uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + std::snprintf(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint)); + bytes += 6; + } + else + { + std::snprintf(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0 + (codepoint >> 10)), + static_cast(0xDC00 + (codepoint & 0x3FF))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast(byte); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str())); + } + + default: // decode found yet incomplete multi-byte code point + { + if (not ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + break; + } + } + } + + if (JSON_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + std::stringstream ss; + ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast(static_cast(s.back())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str())); + } + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template::value or + std::is_same::value, + int> = 0> + void dump_integer(NumberType x) + { + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + const bool is_negative = (x <= 0) and (x != 0); // see issue #755 + std::size_t i = 0; + + while (x != 0) + { + // spare 1 byte for '\0' + assert(i < number_buffer.size() - 1); + + const auto digit = std::labs(static_cast(x % 10)); + number_buffer[i++] = static_cast('0' + digit); + x /= 10; + } + + if (is_negative) + { + // make sure there is capacity for the '-' + assert(i < number_buffer.size() - 2); + number_buffer[i++] = '-'; + } + + std::reverse(number_buffer.begin(), number_buffer.begin() + i); + o->write_characters(number_buffer.data(), i); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (not std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 and std::numeric_limits::digits == 24 and std::numeric_limits::max_exponent == 128) or + (std::numeric_limits::is_iec559 and std::numeric_limits::digits == 53 and std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + char* begin = number_buffer.data(); + char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + assert(len > 0); + // check if buffer was large enough + assert(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + assert((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') + { + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return (c == '.' or c == 'e'); + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 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, // 00..1F + 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, // 20..3F + 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, // 40..5F + 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..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + const uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6) + : static_cast(0xff >> type) & (byte); + + state = utf8d[256u + state * 16u + type]; + return state; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; +}; +} +} + +// #include + + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + {} + + json_ref(const value_type& value) + : value_ref(const_cast(&value)), is_rvalue(false) + {} + + json_ref(std::initializer_list init) + : owned_value(init), value_ref(&owned_value), is_rvalue(true) + {} + + template + json_ref(Args&& ... args) + : owned_value(std::forward(args)...), value_ref(&owned_value), is_rvalue(true) + {} + + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + + value_type moved_or_copied() const + { + if (is_rvalue) + { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const& operator*() const + { + return *static_cast(value_ref); + } + + value_type const* operator->() const + { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue; +}; +} +} + +// #include + + +#include // assert +#include // accumulate +#include // string +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw out_of_range.404 if string @a s could not be converted to an integer + */ + static int array_index(const std::string& s) + { + std::size_t processed_chars = 0; + const int res = std::stoi(s, &processed_chars); + + // check if the string was completely read + if (JSON_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + return res; + } + + private: + /*! + @brief remove and return last reference pointer + @throw out_of_range.405 if JSON pointer has no parent + */ + std::string pop_back() + { + if (JSON_UNLIKELY(is_root())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } + + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } + + json_pointer top() const + { + if (JSON_UNLIKELY(is_root())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->m_type) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + JSON_TRY + { + result = &result->operator[](static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->m_type == detail::value_t::null) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const char x) + { + return (x >= '0' and x <= '9'); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->m_type) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // use unchecked array access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->m_type) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_UNLIKELY(pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } + + /// escape "~"" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.m_type) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_UNLIKELY(not value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_UNLIKELY(not element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return (lhs.reference_tokens == rhs.reference_tokens); + } + + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return not (lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} + +// #include + + +#include + +// #include + +// #include + + +namespace nlohmann +{ +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static void from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static void to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; +} + + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) + +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): + JSON values have + [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](http://en.cppreference.com/w/cpp/concept/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@internal +@note ObjectType trick from http://stackoverflow.com/a/9860911 +@endinternal + +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + friend ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer; + using parser = ::nlohmann::detail::parser; + + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// @copydoc nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /*! + @brief returns version information on the library + + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. + + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + + @liveexample{The following code shows an example output of the `meta()` + function.,meta} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @complexity Constant. + + @since 2.1.0 + */ + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /*! + @brief a type for an object + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, it is unspecified which + one of the values for a given key will be chosen. For instance, + `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or + `{"key": 2}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType>>; + + /*! + @brief a type for an array + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + + #### String comparison + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /// @} + + private: + + /// helper for exception-safe object creation + template + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * object) + { + AllocatorTraits::deallocate(alloc, object, 1); + }; + std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); + assert(object != nullptr); + return object.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.1.1")); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for rvalue strings + json_value(string_t&& value) + { + string = create(std::move(value)); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for rvalue objects + json_value(object_t&& value) + { + object = create(std::move(value)); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + + /// constructor for rvalue arrays + json_value(array_t&& value) + { + array = create(std::move(value)); + } + + void destroy(value_t t) noexcept + { + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + default: + { + break; + } + } + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const noexcept + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief parser event types + + The parser callback distinguishes the following events: + - `object_start`: the parser read `{` and started to process a JSON object + - `key`: the parser read a key of a value in an object + - `object_end`: the parser read `}` and finished processing a JSON object + - `array_start`: the parser read `[` and started to process a JSON array + - `array_end`: the parser read `]` and finished processing a JSON array + - `value`: the parser finished reading a JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + @sa @ref parser_callback_t for more information and examples + */ + using parse_event_t = typename parser::parse_event_t; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse, it is called on certain events + (passed as @ref parse_event_t via parameter @a event) with a set recursion + depth @a depth and context JSON value @a parsed. The return value of the + callback function is a boolean indicating whether the element that emitted + the callback shall be kept or not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa @ref parse for examples + + @since version 1.0.0 + */ + using parser_callback_t = typename parser::parser_callback_t; + + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @param[in] v the type of the value to create + + @complexity Constant. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa @ref clear() -- restores the postcondition of this constructor + + @since version 1.0.0 + */ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create a JSON value + + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exists. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). + + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, + `std::multiset`, and `std::unordered_multiset` with a `value_type` from + which a @ref basic_json value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. + + See the examples below. + + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method + + @tparam U = `uncvref_t` + + @param[in] val the value to be forwarded to the respective constructor + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 + */ + template , + detail::enable_if_t< + detail::is_compatible_type::value, int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept( + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has no way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(initializer_list_t) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(initializer_list_t) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(initializer_list_t) and + @ref object(initializer_list_t). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(initializer_list_t) + for an example. + + @complexity Linear in the size of the initializer list @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string()); + }); + + // adjust type if type deduction is not wanted + if (not type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + std::for_each(init.begin(), init.end(), [this](const detail::json_ref& element_ref) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + assert_invariant(); + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(initializer_list_t, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(initializer_list_t), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(initializer_list_t, bool, value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(initializer_list_t, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @post `std::distance(begin(),end()) == cnt` holds. + + @complexity Linear in @a cnt. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of a null type, invalid_iterator.206 is thrown. + - In case of other primitive types (number, boolean, or string), @a first + must be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector` or `std::map`; that is, a JSON array + or object is constructed from the values in the range. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion (see warning).** If + assertions are switched off, a violation of this precondition yields + undefined behavior. + + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. A violation of this precondition + yields undefined behavior. + + @warning A precondition is enforced with a runtime assertion that will + result in calling `std::abort` if this precondition is not met. + Assertions can be disabled by defining `NDEBUG` at compile time. + See http://en.cppreference.com/w/cpp/error/assert for more + information. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) + { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + break; + } + + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + std::string(first.m_object->type_name()))); + } + + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + /// @private + basic_json(const detail::json_ref& ref) + : basic_json(ref.moved_or_copied()) + {} + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @post `*this == other` + + @complexity Linear in the size of @a other. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + default: + break; + } + + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post `*this` has the same value as @a other before the call. + @post @a other is a JSON null value. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible) + requirements. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the `swap()` member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + reference& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() noexcept + { + assert_invariant(); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + and @a ensure_ascii parameters. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + + @return string containing the serialization of the JSON value + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded + + @complexity Linear. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @liveexample{The following example shows the effect of different @a indent\, + @a indent_char\, and @a ensure_ascii parameters to the result of the + serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0; indentation character @a indent_char, option + @a ensure_ascii and exceptions added in version 3.0.0 + */ + string_t dump(const int indent = -1, const char indent_char = ' ', + const bool ensure_ascii = false) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + Value type | return value + ------------------------- | ------------------------- + null | value_t::null + boolean | value_t::boolean + string | value_t::string + number (integer) | value_t::number_integer + number (unsigned integer) | value_t::number_unsigned + number (floating-point) | value_t::number_float + object | value_t::object + array | value_t::array + discarded | value_t::discarded + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true if and only if the JSON type is primitive + (string, number, boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() or is_string() or is_boolean() or is_number(); + } + + /*! + @brief return whether type is structured + + This function returns true if and only if the JSON type is structured + (array or object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() or is_object(); + } + + /*! + @brief return whether value is null + + This function returns true if and only if the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return (m_type == value_t::null); + } + + /*! + @brief return whether value is a boolean + + This function returns true if and only if the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return (m_type == value_t::boolean); + } + + /*! + @brief return whether value is a number + + This function returns true if and only if the JSON value is a number. This + includes both integer (signed and unsigned) and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() or is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true if and only if the JSON value is a signed or + unsigned integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return (m_type == value_t::number_integer or m_type == value_t::number_unsigned); + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true if and only if the JSON value is an unsigned + integer number. This excludes floating-point and signed integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return (m_type == value_t::number_unsigned); + } + + /*! + @brief return whether value is a floating-point number + + This function returns true if and only if the JSON value is a + floating-point number. This excludes signed and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return (m_type == value_t::number_float); + } + + /*! + @brief return whether value is an object + + This function returns true if and only if the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return (m_type == value_t::object); + } + + /*! + @brief return whether value is an array + + This function returns true if and only if the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return (m_type == value_t::array); + } + + /*! + @brief return whether value is a string + + This function returns true if and only if the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return (m_type == value_t::string); + } + + /*! + @brief return whether value is discarded + + This function returns true if and only if the JSON value was discarded + during parsing with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return (m_type == value_t::discarded); + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @sa @ref type() -- return the type of the JSON value (explicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr::type>(); + + if (JSON_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::type, basic_json_t>::value, + int> = 0> + basic_json get() const + { + return *this; + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template, + detail::enable_if_t < + not std::is_same::value and + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int> = 0> + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); + + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template, + detail::enable_if_t::value and + detail::has_non_default_from_json::value, + int> = 0> + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + PointerType get() noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int>::type = 0> + constexpr const PointerType get() const noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + PointerType get_ptr() noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a reference value (implicit) + + Implicit reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise + + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same>::value and + not std::is_same::value +#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 + and not std::is_same>::value +#endif +#if defined(JSON_HAS_CPP_17) + and not std::is_same::value +#endif + , int >::type = 0 > + operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} + */ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array; in that case, + using the [] operator with an index makes no sense. + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_LIKELY(is_object())) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_LIKELY(is_object())) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name()))); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + template::value, int>::type = 0> + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return *it; + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.306 if the JSON value is not an objec; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this); + } + JSON_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, or boolean values, a + reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, or boolean values, a + reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings: linear in the length of the string + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_UNLIKELY(this != first.m_object or this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_LIKELY(is_array())) + { + if (JSON_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + + @param[in] key key value of the element to search for. + + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @since version 1.0.0 + */ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(KeyT&&) + */ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @note This method always returns `0` when executed on a JSON type that is + not an object. + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without iterator_wrapper: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without iterator proxy: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with iterator proxy: + + @code{cpp} + for (auto it : json::iterator_wrapper(j_object)) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @param[in] ref reference to a JSON value + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @note The name of this function is not yet final and may change in the + future. + + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use @ref items() instead; + that is, replace `json::iterator_wrapper(j)` with `j.items()`. + */ + JSON_DEPRECATED + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + JSON_DEPRECATED + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto it : j_object.items()) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). For primitive types (e.g., numbers), + `key()` returns an empty string. + + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 3.x.x. + */ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /*! + @copydoc items() + */ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty. + + Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called with the current value + type from @ref type(): + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + + @post Has the same effect as calling + @code {.cpp} + *this = basic_json(type()); + @endcode + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @complexity Linear in the size of the JSON value. + + @iterators All iterators, pointers and references related to this container + are invalidated. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @sa @ref basic_json(value_t) -- constructor that creates an object with the + same value than calling `clear()` + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + default: + break; + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_UNLIKELY(not(is_null() or is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // invalidate object + val.m_type = value_t::null; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_UNLIKELY(not(is_null() or is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + m_value.array->push_back(val); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_UNLIKELY(not(is_null() or is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array + m_value.object->insert(val); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param[in] init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(initializer_list_t init) + { + if (is_object() and init.size() == 2 and (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(initializer_list_t) + */ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_UNLIKELY(not(is_null() or is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_UNLIKELY(not(is_null() or is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); + return result; + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + return result; + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_UNLIKELY(not is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (JSON_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // check if range iterators belong to the same JSON object + if (JSON_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + if (JSON_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert( + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); + return result; + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_UNLIKELY(not is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (JSON_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end()); + return result; + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)`. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. + + @liveexample{The example shows how `insert()` is used.,insert__range_object} + + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_UNLIKELY(not is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if range iterators belong to the same JSON object + if (JSON_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (JSON_UNLIKELY(not first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from JSON object @a j and overwrites existing keys. + + @param[in] j JSON object to read values from + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_reference j) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_UNLIKELY(not is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + if (JSON_UNLIKELY(not j.is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + } + + for (auto it = j.cbegin(); it != j.cend(); ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from from range `[first, last)` and overwrites existing + keys. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used__range.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_iterator first, const_iterator last) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_UNLIKELY(not is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + + // check if range iterators belong to the same JSON object + if (JSON_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (JSON_UNLIKELY(not first.m_object->is_object() + or not first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + for (auto it = first; it != last; ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (JSON_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other object to exchange the contents with + + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) + { + // swap only works for objects + if (JSON_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) + { + // swap only works for strings + if (JSON_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note than two NaN values are always treated as unequal. + - Two JSON null values are equal. + + @note Floating-point inside JSON values numbers are compared with + `json::number_float_t::operator==` which is `double::operator==` by + default. To compare floating-point while respecting an epsilon, an alternative + [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + @code {.cpp} + template::value, T>::type> + inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + @endcode + + @note NaN values never compare equal to themselves or to other NaN values. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return (*lhs.m_value.array == *rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object == *rhs.m_value.object); + + case value_t::null: + return true; + + case value_t::string: + return (*lhs.m_value.string == *rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean == rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer == rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float == rhs.m_value.number_float); + + default: + return false; + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return (static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float); + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return (lhs.m_value.number_float == static_cast(rhs.m_value.number_integer)); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return (static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float); + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return (lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned)); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return (static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return (lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned)); + } + + return false; + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs == basic_json(rhs)); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) == rhs); + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs == rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs != basic_json(rhs)); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) != rhs); + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return *lhs.m_value.object < *rhs.m_value.object; + + case value_t::null: + return false; + + case value_t::string: + return *lhs.m_value.string < *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean < rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer < rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float < rhs.m_value.number_float; + + default: + return false; + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs < basic_json(rhs)); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) < rhs); + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return not (rhs < lhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs <= basic_json(rhs)); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) <= rhs); + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs <= rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs > basic_json(rhs)); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) > rhs); + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs < rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs >= basic_json(rhs)); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) >= rhs); + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. + + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + - The indentation character can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0; indentation character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use + @ref operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_DEPRECATED + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } + + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from a compatible input + + This function reads from a compatible input. Examples are: + - an array of 1-byte values + - strings with character/literal type with size of 1 byte + - input streams + - container with contiguous storage of 1-byte values. Compatible container + types include `std::vector`, `std::string`, `std::array`, + `std::valarray`, and `std::initializer_list`. Furthermore, C-style + arrays can be used with `std::begin()`/`std::end()`. User-defined + containers can be used as long as they implement random-access iterators + and a contiguous storage. + + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @param[in] i input to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + + @return result of the deserialization + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 (contiguous containers) + */ + static basic_json parse(detail::input_adapter i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(i, cb, allow_exceptions).parse(true, result); + return result; + } + + /*! + @copydoc basic_json parse(detail::input_adapter, const parser_callback_t) + */ + static basic_json parse(detail::input_adapter& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(i, cb, allow_exceptions).parse(true, result); + return result; + } + + static bool accept(detail::input_adapter i) + { + return parser(i).accept(true); + } + + static bool accept(detail::input_adapter& i) + { + return parser(i).accept(true); + } + + /*! + @brief deserialize from an iterator range with contiguous storage + + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. + + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** + + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. + + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return result of the deserialization + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} + + @since version 2.0.3 + */ + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); + return result; + } + + template::iterator_category>::value, int>::type = 0> + static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter(first, last)).accept(true); + } + + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in + version 4.0.0 of the library. Please use + @ref operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_DEPRECATED + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } + + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return a string representation of a the @a m_type member: + Value type | return value + ----------- | ------------- + null | `"null"` + boolean | `"boolean"` + string | `"string"` + number | `"number"` (for all number types) + object | `"object"` + array | `"array"` + discarded | `"discarded"` + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @sa @ref type() -- return the type of the JSON value + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + + @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` + since 3.0.0 + */ + const char* type_name() const noexcept + { + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } + } + } + + + private: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /*! + @brief create a CBOR serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): + + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | ---------------------------------- | --------------- + null | `null` | Null | 0xF6 + boolean | `true` | True | 0xF5 + boolean | `false` | False | 0xF4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B + number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A + number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 + number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 + number_integer | -24..-1 | Negative integer | 0x20..0x37 + number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_unsigned | 0..23 | Integer | 0x00..0x17 + number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_float | *any value* | Double-Precision Float | 0xFB + string | *length*: 0..23 | UTF-8 string | 0x60..0x77 + string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 + string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 + string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A + string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B + array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B + object | *size*: 0..23 | map | 0xA0..0xB7 + object | *size*: 23..255 | map (1 byte follow) | 0xB8 + object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 + object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA + object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The following CBOR types are not used in the conversion: + - byte strings (0x40..0x5F) + - UTF-8 strings terminated by "break" (0x7F) + - arrays terminated by "break" (0x9F) + - maps terminated by "break" (0xBF) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - tagged items (0xC6..0xD4, 0xD8..0xDB) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + - half and single-precision floats (0xF9-0xFA) + - break (0xFF) + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(detail::input_adapter, const bool strict) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: + + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xC0 + boolean | `true` | true | 0xC3 + boolean | `false` | false | 0xC2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 + number_integer | -2147483648..-32769 | int32 | 0xD2 + number_integer | -32768..-129 | int16 | 0xD1 + number_integer | -128..-33 | int8 | 0xD0 + number_integer | -32..-1 | negative fixint | 0xE0..0xFF + number_integer | 0..127 | positive fixint | 0x00..0x7F + number_integer | 128..255 | uint 8 | 0xCC + number_integer | 256..65535 | uint 16 | 0xCD + number_integer | 65536..4294967295 | uint 32 | 0xCE + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_unsigned | 0..127 | positive fixint | 0x00..0x7F + number_unsigned | 128..255 | uint 8 | 0xCC + number_unsigned | 256..65535 | uint 16 | 0xCD + number_unsigned | 65536..4294967295 | uint 32 | 0xCE + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_float | *any value* | float 64 | 0xCB + string | *length*: 0..31 | fixstr | 0xA0..0xBF + string | *length*: 32..255 | str 8 | 0xD9 + string | *length*: 256..65535 | str 16 | 0xDA + string | *length*: 65536..4294967295 | str 32 | 0xDB + array | *size*: 0..15 | fixarray | 0x90..0x9F + array | *size*: 16..65535 | array 16 | 0xDC + array | *size*: 65536..4294967295 | array 32 | 0xDD + object | *size*: 0..15 | fix map | 0x80..0x8F + object | *size*: 16..65535 | map 16 | 0xDE + object | *size*: 65536..4294967295 | map 32 | 0xDF + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. + + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements + + @note The following MessagePack types are not used in the conversion: + - bin 8 - bin 32 (0xC4..0xC6) + - ext 8 - ext 32 (0xC7..0xC9) + - float 32 (0xCA) + - fixext 1 - fixext 16 (0xD4..0xD8) + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector&, const size_t) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /*! + @brief create a UBJSON serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the UBJSON + (Universal Binary JSON) serialization format. UBJSON aims to be more compact + than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + UBJSON types according to the UBJSON specification: + + JSON value type | value/range | UBJSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | `Z` + boolean | `true` | true | `T` + boolean | `false` | false | `F` + number_integer | -9223372036854775808..-2147483649 | int64 | `L` + number_integer | -2147483648..-32769 | int32 | `l` + number_integer | -32768..-129 | int16 | `I` + number_integer | -128..127 | int8 | `i` + number_integer | 128..255 | uint8 | `U` + number_integer | 256..32767 | int16 | `I` + number_integer | 32768..2147483647 | int32 | `l` + number_integer | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 0..127 | int8 | `i` + number_unsigned | 128..255 | uint8 | `U` + number_unsigned | 256..32767 | int16 | `I` + number_unsigned | 32768..2147483647 | int32 | `l` + number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_float | *any value* | float64 | `D` + string | *with shortest length indicator* | string | `S` + array | *see notes on optimized format* | array | `[` + object | *see notes on optimized format* | map | `{` + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a UBJSON value. + + @note The following values can **not** be converted to a UBJSON value: + - strings with more than 9223372036854775807 bytes (theoretical) + - unsigned integer numbers above 9223372036854775807 + + @note The following markers are not used in the conversion: + - `Z`: no-op values are not created. + - `C`: single-byte strings are serialized with `S` markers. + + @note Any UBJSON output created @ref to_ubjson can be successfully parsed + by @ref from_ubjson. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The optimized formats for containers are supported: Parameter + @a use_size adds size information to the beginning of a container and + removes the closing marker. Parameter @a use_type further checks + whether all elements of a container have the same type and adds the + type marker to the beginning of the container. The @a use_type + parameter must only be used together with @a use_size = true. Note + that @a use_size = true alone may result in larger representations - + the benefit of this parameter is that the receiving side is + immediately informed on the number of elements of the container. + + @param[in] j JSON value to serialize + @param[in] use_size whether to add size annotations to container types + @param[in] use_type whether to add type annotations to container types + (must be combined with @a use_size = true) + @return UBJSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in UBJSON format.,to_ubjson} + + @sa http://ubjson.org + @sa @ref from_ubjson(detail::input_adapter, const bool strict) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + + @since version 3.1.0 + */ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /*! + @brief create a JSON value from an input in CBOR format + + Deserializes a given input @a i to a JSON value using the CBOR (Concise + Binary Object Representation) serialization format. + + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1A + Unsigned integer | number_unsigned | 0x1B + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3A + Negative integer | number_integer | 0x3B + Negative integer | number_integer | 0x40..0x57 + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7A + UTF-8 string | string | 0x7B + UTF-8 string | string | 0x7F + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9A + array | array | 0x9B + array | array | 0x9F + map | object | 0xA0..0xB7 + map | object | 0xB8 + map | object | 0xB9 + map | object | 0xBA + map | object | 0xBB + map | object | 0xBF + False | `false` | 0xF4 + True | `true` | 0xF5 + Nill | `null` | 0xF6 + Half-Precision Float | number_float | 0xF9 + Single-Precision Float | number_float | 0xFA + Double-Precision Float | number_float | 0xFB + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - byte strings (0x40..0x5F) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - tagged items (0xC6..0xD4, 0xD8..0xDB) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + + @param[in] i an input in CBOR format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @return deserialized JSON value + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from CBOR were + used in the given input @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(detail::input_adapter, const bool) for the + related MessagePack format + @sa @ref from_ubjson(detail::input_adapter, const bool) for the related + UBJSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0 + */ + static basic_json from_cbor(detail::input_adapter i, + const bool strict = true) + { + return binary_reader(i).parse_cbor(strict); + } + + /*! + @copydoc from_cbor(detail::input_adapter, const bool) + */ + template::value, int> = 0> + static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true) + { + return binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).parse_cbor(strict); + } + + /*! + @brief create a JSON value from an input in MessagePack format + + Deserializes a given input @a i to a JSON value using the MessagePack + serialization format. + + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7F + fixmap | object | 0x80..0x8F + fixarray | array | 0x90..0x9F + fixstr | string | 0xA0..0xBF + nil | `null` | 0xC0 + false | `false` | 0xC2 + true | `true` | 0xC3 + float 32 | number_float | 0xCA + float 64 | number_float | 0xCB + uint 8 | number_unsigned | 0xCC + uint 16 | number_unsigned | 0xCD + uint 32 | number_unsigned | 0xCE + uint 64 | number_unsigned | 0xCF + int 8 | number_integer | 0xD0 + int 16 | number_integer | 0xD1 + int 32 | number_integer | 0xD2 + int 64 | number_integer | 0xD3 + str 8 | string | 0xD9 + str 16 | string | 0xDA + str 32 | string | 0xDB + array 16 | array | 0xDC + array 32 | array | 0xDD + map 16 | object | 0xDE + map 32 | object | 0xDF + negative fixint | number_integer | 0xE0-0xFF + + @warning The mapping is **incomplete** in the sense that not all + MessagePack types can be converted to a JSON value. The following + MessagePack types are not supported and will yield parse errors: + - bin 8 - bin 32 (0xC4..0xC6) + - ext 8 - ext 32 (0xC7..0xC9) + - fixext 1 - fixext 16 (0xD4..0xD8) + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @param[in] i an input in MessagePack format convertible to an input + adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from MessagePack were + used in the given input @a i or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR + format + @sa @ref from_ubjson(detail::input_adapter, const bool) for the related + UBJSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0 + */ + static basic_json from_msgpack(detail::input_adapter i, + const bool strict = true) + { + return binary_reader(i).parse_msgpack(strict); + } + + /*! + @copydoc from_msgpack(detail::input_adapter, const bool) + */ + template::value, int> = 0> + static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true) + { + return binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).parse_msgpack(strict); + } + + /*! + @brief create a JSON value from an input in UBJSON format + + Deserializes a given input @a i to a JSON value using the UBJSON (Universal + Binary JSON) serialization format. + + The library maps UBJSON types to JSON value types as follows: + + UBJSON type | JSON value type | marker + ----------- | --------------------------------------- | ------ + no-op | *no value, next value is read* | `N` + null | `null` | `Z` + false | `false` | `F` + true | `true` | `T` + float32 | number_float | `d` + float64 | number_float | `D` + uint8 | number_unsigned | `U` + int8 | number_integer | `i` + int16 | number_integer | `I` + int32 | number_integer | `l` + int64 | number_integer | `L` + string | string | `S` + char | string | `C` + array | array (optimized values are supported) | `[` + object | object (optimized values are supported) | `{` + + @note The mapping is **complete** in the sense that any UBJSON value can + be converted to a JSON value. + + @param[in] i an input in UBJSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if a parse error occurs + @throw parse_error.113 if a string could not be parsed successfully + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + UBJSON format to a JSON value.,from_ubjson} + + @sa http://ubjson.org + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + analogous serialization + @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR + format + @sa @ref from_msgpack(detail::input_adapter, const bool) for the related + MessagePack format + + @since version 3.1.0 + */ + static basic_json from_ubjson(detail::input_adapter i, + const bool strict = true) + { + return binary_reader(i).parse_ubjson(strict); + } + + template::value, int> = 0> + static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true) + { + return binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).parse_ubjson(strict); + } + + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer} + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitive values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this function, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.is_root()) + { + result = val; + } + else + { + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_UNLIKELY(static_cast(idx) > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + else + { + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + } + break; + } + + default: + { + // if there exists a parent it cannot be primitive + assert(false); // LCOV_EXCL_LINE + } + } + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [&result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(static_cast(json_pointer::array_index(last_path))); + } + }; + + // type check: top level value must be an array + if (JSON_UNLIKELY(not json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_UNLIKELY(it == val.m_value.object->end())) + { + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + } + + // check if result is of type string + if (JSON_UNLIKELY(string_type and not it->second.is_string())) + { + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_UNLIKELY(not val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + // collect mandatory members + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const std::string from_path = get_value("copy", "from", true); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_UNLIKELY(not success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + } + + break; + } + + case patch_operations::invalid: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to compare from + @param[in] target JSON value to compare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + @sa @ref merge_patch -- apply a JSON Merge Patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + } + else + { + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() and i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/" + std::to_string(i)}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path + "/" + key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path + "/" + key}, + {"value", it.value()} + }); + } + } + + break; + } + + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /*! + @brief applies a JSON Merge Patch + + The merge patch format is primarily intended for use with the HTTP PATCH + method as a means of describing a set of modifications to a target + resource's content. This function applies a merge patch to the current + JSON value. + + The function implements the following algorithm from Section 2 of + [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): + + ``` + define MergePatch(Target, Patch): + if Patch is an Object: + if Target is not an Object: + Target = {} // Ignore the contents and set it to an empty Object + for each Name/Value pair in Patch: + if Value is null: + if Name exists in Target: + remove the Name/Value pair from Target + else: + Target[Name] = MergePatch(Target[Name], Value) + return Target + else: + return Patch + ``` + + Thereby, `Target` is the current object; that is, the patch is applied to + the current value. + + @param[in] patch the patch to apply + + @complexity Linear in the lengths of @a patch. + + @liveexample{The following code shows how a JSON Merge Patch is applied to + a JSON document.,merge_patch} + + @sa @ref patch -- apply a JSON patch + @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) + + @since version 3.0.0 + */ + void merge_patch(const basic_json& patch) + { + if (patch.is_object()) + { + if (not is_object()) + { + *this = object(); + } + for (auto it = patch.begin(); it != patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = patch; + } + } + + /// @} +}; +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ +template<> +inline void swap(nlohmann::json& j1, + nlohmann::json& j2) noexcept( + is_nothrow_move_constructible::value and + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +/// hash value for JSON objects +template<> +struct hash +{ + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const + { + // a naive hashing via the string representation + const auto& h = hash(); + return h(j.dump()); + } +}; + +/// specialization for std::less +/// @note: do not remove the space after '<', +/// see https://github.com/nlohmann/json/pull/679 +template<> +struct less< ::nlohmann::detail::value_t> +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +} // namespace std + +/*! +@brief user-defined string literal for JSON values + +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. + +@param[in] s a string representation of a JSON object +@param[in] n the length of string @a s +@return a JSON object + +@since version 1.0.0 +*/ +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s +@return a JSON pointer object + +@since version 2.0.0 +*/ +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop +#endif +#if defined(__clang__) + #pragma GCC diagnostic pop +#endif + +// clean up +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_LIKELY +#undef JSON_UNLIKELY +#undef JSON_DEPRECATED +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef NLOHMANN_JSON_HAS_HELPER + + +#endif diff --git a/hilti/include/3rdparty/pathfind b/hilti/include/3rdparty/pathfind new file mode 120000 index 000000000..75b163470 --- /dev/null +++ b/hilti/include/3rdparty/pathfind @@ -0,0 +1 @@ +../../src/3rdparty/pathfind/src \ No newline at end of file diff --git a/hilti/include/3rdparty/tinyformat/README.md b/hilti/include/3rdparty/tinyformat/README.md new file mode 100644 index 000000000..fc8856c4d --- /dev/null +++ b/hilti/include/3rdparty/tinyformat/README.md @@ -0,0 +1,465 @@ +# tinyformat.h + +## A minimal type safe printf() replacement + +**tinyformat.h** is a type safe printf replacement library in a single C++ +header file. If you've ever wanted `printf("%s", s)` to just work regardless +of the type of `s`, tinyformat might be for you. Design goals include: + +* Type safety and extensibility for user defined types. +* C99 `printf()` compatibility, to the extent possible using `std::ostream` +* POSIX extension for positional arguments +* Simplicity and minimalism. A single header file to include and distribute + with your projects. +* Augment rather than replace the standard stream formatting mechanism +* C++98 support, with optional C++11 niceties + +Build status, master branch: +[![Linux/OSX build](https://travis-ci.org/c42f/tinyformat.svg?branch=master)](https://travis-ci.org/c42f/tinyformat) +[![Windows build](https://ci.appveyor.com/api/projects/status/rwxqhhy6v5m0p1aq/branch/master?svg=true)](https://ci.appveyor.com/project/c42f/tinyformat/branch/master) + +## Quickstart + +To print a date to `std::cout`: + +```C++ +std::string weekday = "Wednesday"; +const char* month = "July"; +size_t day = 27; +long hour = 14; +int min = 44; + +tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); +``` + +POSIX extension for positional arguments is available. +The ability to rearrange formatting arguments is an important feature +for localization because the word order may vary in different languages. + +Previous example for German usage. Arguments are reordered: + +```C++ +tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min); +``` + +The strange types here emphasize the type safety of the interface, for example +it is possible to print a `std::string` using the `"%s"` conversion, and a +`size_t` using the `"%d"` conversion. A similar result could be achieved +using either of the `tfm::format()` functions. One prints on a user provided +stream: + +```C++ +tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", + weekday, month, day, hour, min); +``` + +The other returns a `std::string`: + +```C++ +std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", + weekday, month, day, hour, min); +std::cout << date; +``` + + +It is safe to use tinyformat inside a template function. For any type which +has the usual stream insertion `operator<<` defined, the following will work +as desired: + +```C++ +template +void myPrint(const T& value) +{ + tfm::printf("My value is '%s'\n", value); +} +``` + +(The above is a compile error for types `T` without a stream insertion +operator.) + + +## Function reference + +All user facing functions are defined in the namespace `tinyformat`. A +namespace alias `tfm` is provided to encourage brevity, but can easily be +disabled if desired. + +Three main interface functions are available: an iostreams-based `format()`, +a string-based `format()` and a `printf()` replacement. These functions +can be thought of as C++ replacements for C's `fprintf()`, `sprintf()` and +`printf()` functions respectively. All the interface functions can take an +unlimited number of input arguments if compiled with C++11 variadic templates +support. In C++98 mode, the number of arguments must be limited to some fixed +upper bound which is currently 16 as of version 1.3. Supporting more arguments +is quite easy using the in-source code generator based on +[cog.py](http://nedbatchelder.com/code/cog) - see the source for details. + +The `format()` function which takes a stream as the first argument is the +main part of the tinyformat interface. `stream` is the output stream, +`formatString` is a format string in C99 `printf()` format, and the values +to be formatted have arbitrary types: + +```C++ +template +void format(std::ostream& stream, const char* formatString, + const Args&... args); +``` + +The second version of `format()` is a convenience function which returns a +`std::string` rather than printing onto a stream. This function simply +calls the main version of `format()` using a `std::ostringstream`, and +returns the resulting string: + +```C++ +template +std::string format(const char* formatString, const Args&... args); +``` + +Finally, `printf()` and `printfln()` are convenience functions which call +`format()` with `std::cout` as the first argument; both have the same +signature: + +```C++ +template +void printf(const char* formatString, const Args&... args); +``` + +`printfln()` is the same as `printf()` but appends an additional newline +for convenience - a concession to the author's tendency to forget the newline +when using the library for simple logging. + +## Format strings and type safety + +Tinyformat parses C99 format strings to guide the formatting process --- please +refer to any standard C99 printf documentation for format string syntax. In +contrast to printf, tinyformat does not use the format string to decide on +the type to be formatted so this does not compromise the type safety: *you may +use any format specifier with any C++ type*. The author suggests standardising +on the `%s` conversion unless formatting numeric types. + +Let's look at what happens when you execute the function call: + +```C++ +tfm::format(outStream, "%+6.4f", yourType); +``` + +First, the library parses the format string, and uses it to modify the state of +`outStream`: + +1. The `outStream` formatting flags are cleared and the width, precision and + fill reset to the default. +2. The flag `'+'` means to prefix positive numbers with a `'+'`; tinyformat + executes `outStream.setf(std::ios::showpos)` +3. The number 6 gives the field width; execute `outStream.width(6)`. +4. The number 4 gives the precision; execute `outStream.precision(4)`. +5. The conversion specification character `'f'` means that floats should be + formatted with a fixed number of digits; this corresponds to executing + `outStream.setf(std::ios::fixed, std::ios::floatfield);` + +After all these steps, tinyformat executes: + +```C++ +outStream << yourType; +``` + +and finally restores the stream flags, precision and fill. + +What happens if `yourType` isn't actually a floating point type? In this +case the flags set above are probably irrelevant and will be ignored by the +underlying `std::ostream` implementation. The field width of six may cause +some padding in the output of `yourType`, but that's about it. + + +### Special cases for "%p", "%c" and "%s" + +Tinyformat normally uses `operator<<` to convert types to strings. However, +the "%p" and "%c" conversions require special rules for robustness. Consider: + +```C++ +uint8_t* pixels = get_pixels(/* ... */); +tfm::printf("%p", pixels); +``` + +Clearly the intention here is to print a representation of the *pointer* to +`pixels`, but since `uint8_t` is a character type the compiler would +attempt to print it as a C string if we blindly fed it into `operator<<`. To +counter this kind of madness, tinyformat tries to static_cast any type fed to +the "%p" conversion into a `const void*` before printing. If this can't be +done at compile time the library falls back to using `operator<<` as usual. + +The "%c" conversion has a similar problem: it signifies that the given integral +type should be converted into a `char` before printing. The solution is +identical: attempt to convert the provided type into a char using +`static_cast` if possible, and if not fall back to using `operator<<`. + +The "%s" conversion sets the boolalpha flag on the formatting stream. This +means that a `bool` variable printed with "%s" will come out as `true` or +`false` rather than the `1` or `0` that you would otherwise get. + + +### Incompatibilities with C99 printf + +Not all features of printf can be simulated simply using standard iostreams. +Here's a list of known incompatibilities: + +* The `"%a"` and `"%A"` hexadecimal floating point conversions ignore precision + as stream output of hexfloat (introduced in C++11) ignores precision, always + outputting the minimum number of digits required for exact representation. + MSVC incorrectly honors stream precision, so we force precision to 13 in this + case to guarentee lossless roundtrip conversion. +* The precision for integer conversions cannot be supported by the iostreams + state independently of the field width. (Note: **this is only a + problem for certain obscure integer conversions**; float conversions like + `%6.4f` work correctly.) In tinyformat the field width takes precedence, + so the 4 in `%6.4d` will be ignored. However, if the field width is not + specified, the width used internally is set equal to the precision and padded + with zeros on the left. That is, a conversion like `%.4d` effectively + becomes `%04d` internally. This isn't correct for every case (eg, negative + numbers end up with one less digit than desired) but it's about the closest + simple solution within the iostream model. +* The `"%n"` query specifier isn't supported to keep things simple and will + result in a call to `TINYFORMAT_ERROR`. +* The `"%ls"` conversion is not supported, and attempting to format a + `wchar_t` array will cause a compile time error to minimise unexpected + surprises. If you know the encoding of your wchar_t strings, you could write + your own `std::ostream` insertion operator for them, and disable the + compile time check by defining the macro `TINYFORMAT_ALLOW_WCHAR_STRINGS`. + If you want to print the *address* of a wide character with the `"%p"` + conversion, you should cast it to a `void*` before passing it to one of the + formatting functions. + + +## Error handling + +By default, tinyformat calls `assert()` if it encounters an error in the +format string or number of arguments. This behaviour can be changed (for +example, to throw an exception) by defining the `TINYFORMAT_ERROR` macro +before including tinyformat.h, or editing the config section of the header. + + +## Formatting user defined types + +User defined types with a stream insertion operator will be formatted using +`operator<<(std::ostream&, T)` by default. The `"%s"` format specifier is +suggested for user defined types, unless the type is inherently numeric. + +For further customization, the user can override the `formatValue()` +function to specify formatting independently of the stream insertion operator. +If you override this function, the library will have already parsed the format +specification and set the stream flags accordingly - see the source for details. + + +## Wrapping tfm::format() inside a user defined format function + +Suppose you wanted to define your own function which wraps `tfm::format`. +For example, consider an error function taking an error code, which in C++11 +might be written simply as: + +```C++ +template +void error(int code, const char* fmt, const Args&... args) +{ + std::cerr << "error (code " << code << ")"; + tfm::format(std::cerr, fmt, args...); +} +``` + +Simulating this functionality in C++98 is pretty painful since it requires +writing out a version of `error()` for each desired number of arguments. To +make this bearable tinyformat comes with a set of macros which are used +internally to generate the API, but which may also be used in user code. + +The three macros `TINYFORMAT_ARGTYPES(n)`, `TINYFORMAT_VARARGS(n)` and +`TINYFORMAT_PASSARGS(n)` will generate a list of `n` argument types, +type/name pairs and argument names respectively when called with an integer +`n` between 1 and 16. We can use these to define a macro which generates the +desired user defined function with `n` arguments. This should be followed by +a call to `TINYFORMAT_FOREACH_ARGNUM` to generate the set of functions for +all supported `n`: + +```C++ +#define MAKE_ERROR_FUNC(n) \ +template \ +void error(int code, const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + std::cerr << "error (code " << code << ")"; \ + tfm::format(std::cerr, fmt, TINYFORMAT_PASSARGS(n)); \ +} +TINYFORMAT_FOREACH_ARGNUM(MAKE_ERROR_FUNC) +``` + +Sometimes it's useful to be able to pass a list of format arguments through to +a non-template function. The `FormatList` class is provided as a way to do +this by storing the argument list in a type-opaque way. For example: + +```C++ +template +void error(int code, const char* fmt, const Args&... args) +{ + tfm::FormatListRef formatList = tfm::makeFormatList(args...); + errorImpl(code, fmt, formatList); +} +``` + +What's interesting here is that `errorImpl()` is a non-template function so +it could be separately compiled if desired. The `FormatList` instance can be +used via a call to the `vformat()` function (the name chosen for semantic +similarity to `vprintf()`): + +```C++ +void errorImpl(int code, const char* fmt, tfm::FormatListRef formatList) +{ + std::cerr << "error (code " << code << ")"; + tfm::vformat(std::cout, fmt, formatList); +} +``` + +The construction of a `FormatList` instance is very lightweight - it defers +all formatting and simply stores a couple of function pointers and a value +pointer per argument. Since most of the actual work is done inside +`vformat()`, any logic which causes an early exit of `errorImpl()` - +filtering of verbose log messages based on error code for example - could be a +useful optimization for programs using tinyformat. (A faster option would be +to write any early bailout code inside `error()`, though this must be done in +the header.) + + +## Benchmarks + +### Compile time and code bloat + +The script `bloat_test.sh` included in the repository tests whether +tinyformat succeeds in avoiding compile time and code bloat for nontrivial +projects. The idea is to include `tinyformat.h` into 100 translation units +and use `printf()` five times in each to simulate a medium sized project. +The resulting executable size and compile time (g++-4.8.2, linux ubuntu 14.04) +is shown in the following tables, which can be regenerated using `make +bloat_test`: + +**Non-optimized build** + +| test name | compiler wall time | executable size (stripped) | +| ---------------------- | ------------------ | -------------------------- | +| libc printf | 1.8s | 48K (36K) | +| std::ostream | 10.7s | 96K (76K) | +| tinyformat, no inlines | 18.9s | 140K (104K) | +| tinyformat | 21.1s | 220K (180K) | +| tinyformat, c++0x mode | 20.7s | 220K (176K) | +| boost::format | 70.1s | 844K (736K) | + +**Optimized build (-O3 -DNDEBUG)** + +| test name | compiler wall time | executable size (stripped) | +| ---------------------- | ------------------ | -------------------------- | +| libc printf | 2.3s | 40K (28K) | +| std::ostream | 11.8s | 104K (80K) | +| tinyformat, no inlines | 23.0s | 128K (104K) | +| tinyformat | 32.9s | 128K (104K) | +| tinyformat, c++0x mode | 34.0s | 128K (104K) | +| boost::format | 147.9s | 644K (600K) | + +For large projects it's arguably worthwhile to do separate compilation of the +non-templated parts of tinyformat, as shown in the rows labelled *tinyformat, +no inlines*. These were generated by putting the implementation of `vformat` +(`detail::formatImpl()` etc) it into a separate file, tinyformat.cpp. Note +that the results above can vary considerably with different compilers. For +example, the `-fipa-cp-clone` optimization pass in g++-4.6 resulted in +excessively large binaries. On the other hand, the g++-4.8 results are quite +similar to using clang++-3.4. + + +### Speed tests + +The following speed tests results were generated by building +`tinyformat_speed_test.cpp` on an Intel core i7-2600K running Linux Ubuntu +14.04 with g++-4.8.2 using `-O3 -DNDEBUG`. In the test, the format string +`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` is filled 2000000 times with output sent to +`/dev/null`; for further details see the source and Makefile. + +| test name | run time | +| -------------- | -------- | +| libc printf | 1.20s | +| std::ostream | 1.82s | +| tinyformat | 2.08s | +| boost::format | 9.04s | + +It's likely that tinyformat has an advantage over boost.format because it tries +reasonably hard to avoid formatting into temporary strings, preferring instead +to send the results directly to the stream buffer. Tinyformat cannot +be faster than the iostreams because it uses them internally, but it comes +acceptably close. + + +## Rationale + +Or, why did I reinvent this particularly well studied wheel? + +Nearly every program needs text formatting in some form but in many cases such +formatting is *incidental* to the main purpose of the program. In these cases, +you really want a library which is simple to use but as lightweight as +possible. + +The ultimate in lightweight dependencies are the solutions provided by the C++ +and C libraries. However, both the C++ iostreams and C's printf() have +well known usability problems: iostreams are hopelessly verbose for complicated +formatting and printf() lacks extensibility and type safety. For example: + +```C++ +// Verbose; hard to read, hard to type: +std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; +// The alternative using a format string is much easier on the eyes +tfm::printf("%.2f\n", 1.23456); + +// Type mismatch between "%s" and int: will cause a segfault at runtime! +printf("%s", 1); +// The following is perfectly fine, and will result in "1" being printed. +tfm::printf("%s", 1); +``` + +On the other hand, there are plenty of excellent and complete libraries which +solve the formatting problem in great generality (boost.format and fastformat +come to mind, but there are many others). Unfortunately these kind of +libraries tend to be rather heavy dependencies, far too heavy for projects +which need to do only a little formatting. Problems include + +1. Having many large source files. This makes a heavy dependency unsuitable to + bundle within other projects for convenience. +2. Slow build times for every file using any sort of formatting (this is very + noticeable with g++ and boost/format.hpp. I'm not sure about the various + other alternatives.) +3. Code bloat due to instantiating many templates + +Tinyformat tries to solve these problems while providing formatting which is +sufficiently general and fast for incidental day to day uses. + + +## License + +For minimum license-related fuss, tinyformat.h is distributed under the boost +software license, version 1.0. (Summary: you must keep the license text on +all source copies, but don't have to mention tinyformat when distributing +binaries.) + + +## Author and acknowledgements + +Tinyformat was written by Chris Foster, with contributions from various people +as recorded in the git repository. +The implementation owes a lot to `boost::format` for showing that it's fairly +easy to use stream based formatting to simulate most of the `printf()` +syntax. Douglas Gregor's introduction to variadic templates --- see +https://web.archive.org/web/20131018185034/http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html --- was +also helpful, especially since it solves exactly the `printf()` problem for +the case of trivial format strings. + +## Bugs + +Here's a list of known bugs which are probably cumbersome to fix: + +* Field padding won't work correctly with complicated user defined types. For + general types, the only way to do this correctly seems to be format to a + temporary string stream, check the length, and finally send to the output + stream with padding if necessary. Doing this for all types would be + quite inelegant because it implies extra allocations to make the temporary + stream. A workaround is to add logic to `operator<<()` for composite user + defined types so they are aware of the stream field width. diff --git a/hilti/include/3rdparty/tinyformat/bloat_test.sh b/hilti/include/3rdparty/tinyformat/bloat_test.sh new file mode 100755 index 000000000..8f6961fe3 --- /dev/null +++ b/hilti/include/3rdparty/tinyformat/bloat_test.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Script to test how much bloating a large project will suffer when using +# tinyformat, vs alternatives. Call as +# +# C99 printf : bloat_test.sh $CXX [-O3] +# tinyformat : bloat_test.sh $CXX [-O3] -DUSE_TINYFORMAT +# tinyformat, no inlines: bloat_test.sh $CXX [-O3] -DUSE_TINYFORMAT -DUSE_TINYFORMAT_NOINLINE +# boost::format : bloat_test.sh $CXX [-O3] -DUSE_BOOST +# std::iostream : bloat_test.sh $CXX [-O3] -DUSE_IOSTREAMS +# +# Note: to test the NOINLINE version of tinyformat, you need to remove the few +# inline functions in the tinyformat::detail namespace, and put them into a +# file tinyformat.cpp. Then rename that version of tinyformat.h into +# tinyformat_noinline.h + + +prefix=_bloat_test_tmp_ +numTranslationUnits=100 + +rm -f $prefix??.cpp ${prefix}main.cpp ${prefix}all.h + +template=' +#ifdef USE_BOOST + +#include +#include + +void doFormat_a() +{ + std::cout << boost::format("%s\n") % "somefile.cpp"; + std::cout << boost::format("%s:%d\n") % "somefile.cpp"% 42; + std::cout << boost::format("%s:%d:%s\n") % "somefile.cpp"% 42% "asdf"; + std::cout << boost::format("%s:%d:%d:%s\n") % "somefile.cpp"% 42% 1% "asdf"; + std::cout << boost::format("%s:%d:%d:%d:%s\n") % "somefile.cpp"% 42% 1% 2% "asdf"; +} + +#elif defined(USE_IOSTREAMS) + +#include + +void doFormat_a() +{ + const char* str1 = "somefile.cpp"; + const char* str2 = "asdf"; + std::cout << str1 << "\n"; + std::cout << str1 << ":" << 42 << "\n"; + std::cout << str1 << ":" << 42 << ":" << str2 << "\n"; + std::cout << str1 << ":" << 42 << ":" << 1 << ":" << str2 << "\n"; + std::cout << str1 << ":" << 42 << ":" << 1 << ":" << 2 << ":" << str2 << "\n"; +} + +#else +#ifdef USE_TINYFORMAT +# ifdef USE_TINYFORMAT_NOINLINE +# include "tinyformat_noinline.h" +# else +# include "tinyformat.h" +# endif +# define PRINTF tfm::printf +#else +# include +# define PRINTF ::printf +#endif + +void doFormat_a() +{ + const char* str1 = "somefile.cpp"; + const char* str2 = "asdf"; + PRINTF("%s\n", str1); + PRINTF("%s:%d\n", str1, 42); + PRINTF("%s:%d:%s\n", str1, 42, str2); + PRINTF("%s:%d:%d:%s\n", str1, 42, 1, str2); + PRINTF("%s:%d:%d:%d:%s\n", str1, 42, 1, 2, str2); +} +#endif +' + +# Generate all the files +echo "#include \"${prefix}all.h\"" >> ${prefix}main.cpp +echo ' +#ifdef USE_TINYFORMAT_NOINLINE +#include "tinyformat.cpp" +#endif + +int main() +{' >> ${prefix}main.cpp + +for ((i=0;i<$numTranslationUnits;i++)) ; do + n=$(printf "%03d" $i) + f=${prefix}$n.cpp + echo "$template" | sed -e "s/doFormat_a/doFormat_a$n/" -e "s/42/$i/" > $f + echo "doFormat_a$n();" >> ${prefix}main.cpp + echo "void doFormat_a$n();" >> ${prefix}all.h +done + +echo "return 0; }" >> ${prefix}main.cpp + + +# Compile +time "$@" ${prefix}???.cpp ${prefix}main.cpp -o ${prefix}.out +ls -sh ${prefix}.out +cp ${prefix}.out ${prefix}stripped.out +strip ${prefix}stripped.out +ls -sh ${prefix}stripped.out diff --git a/hilti/include/3rdparty/tinyformat/tinyformat.h b/hilti/include/3rdparty/tinyformat/tinyformat.h new file mode 100644 index 000000000..a0deefd39 --- /dev/null +++ b/hilti/include/3rdparty/tinyformat/tinyformat.h @@ -0,0 +1,1170 @@ +// tinyformat.h +// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] +// +// Boost Software License - Version 1.0 +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//------------------------------------------------------------------------------ +// Tinyformat: A minimal type safe printf replacement +// +// tinyformat.h is a type safe printf replacement library in a single C++ +// header file. Design goals include: +// +// * Type safety and extensibility for user defined types. +// * C99 printf() compatibility, to the extent possible using std::ostream +// * POSIX extension for positional arguments +// * Simplicity and minimalism. A single header file to include and distribute +// with your projects. +// * Augment rather than replace the standard stream formatting mechanism +// * C++98 support, with optional C++11 niceties +// +// +// Main interface example usage +// ---------------------------- +// +// To print a date to std::cout for American usage: +// +// std::string weekday = "Wednesday"; +// const char* month = "July"; +// size_t day = 27; +// long hour = 14; +// int min = 44; +// +// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); +// +// POSIX extension for positional arguments is available. +// The ability to rearrange formatting arguments is an important feature +// for localization because the word order may vary in different languages. +// +// Previous example for German usage. Arguments are reordered: +// +// tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min); +// +// The strange types here emphasize the type safety of the interface; it is +// possible to print a std::string using the "%s" conversion, and a +// size_t using the "%d" conversion. A similar result could be achieved +// using either of the tfm::format() functions. One prints on a user provided +// stream: +// +// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", +// weekday, month, day, hour, min); +// +// The other returns a std::string: +// +// std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", +// weekday, month, day, hour, min); +// std::cout << date; +// +// These are the three primary interface functions. There is also a +// convenience function printfln() which appends a newline to the usual result +// of printf() for super simple logging. +// +// +// User defined format functions +// ----------------------------- +// +// Simulating variadic templates in C++98 is pretty painful since it requires +// writing out the same function for each desired number of arguments. To make +// this bearable tinyformat comes with a set of macros which are used +// internally to generate the API, but which may also be used in user code. +// +// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and +// TINYFORMAT_PASSARGS(n) will generate a list of n argument types, +// type/name pairs and argument names respectively when called with an integer +// n between 1 and 16. We can use these to define a macro which generates the +// desired user defined function with n arguments. To generate all 16 user +// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an +// example, see the implementation of printf() at the end of the source file. +// +// Sometimes it's useful to be able to pass a list of format arguments through +// to a non-template function. The FormatList class is provided as a way to do +// this by storing the argument list in a type-opaque way. Continuing the +// example from above, we construct a FormatList using makeFormatList(): +// +// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min); +// +// The format list can now be passed into any non-template function and used +// via a call to the vformat() function: +// +// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList); +// +// +// Additional API information +// -------------------------- +// +// Error handling: Define TINYFORMAT_ERROR to customize the error handling for +// format strings which are unsupported or have the wrong number of format +// specifiers (calls assert() by default). +// +// User defined types: Uses operator<< for user defined types by default. +// Overload formatValue() for more control. + + +#ifndef TINYFORMAT_H_INCLUDED +#define TINYFORMAT_H_INCLUDED + +namespace tinyformat {} +//------------------------------------------------------------------------------ +// Config section. Customize to your liking! + +// Namespace alias to encourage brevity +namespace tfm = tinyformat; + +// Error handling; calls assert() by default. +// #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString) + +// Define for C++11 variadic templates which make the code shorter & more +// general. If you don't define this, C++11 support is autodetected below. +// #define TINYFORMAT_USE_VARIADIC_TEMPLATES + + +//------------------------------------------------------------------------------ +// Implementation details. +#include +#include +#include + +#ifndef TINYFORMAT_ASSERT +# include +# define TINYFORMAT_ASSERT(cond) assert(cond) +#endif + +#ifndef TINYFORMAT_ERROR +# include +# define TINYFORMAT_ERROR(reason) assert(0 && reason) +#endif + +#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) +# ifdef __GXX_EXPERIMENTAL_CXX0X__ +# define TINYFORMAT_USE_VARIADIC_TEMPLATES +# endif +#endif + +#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 +// std::showpos is broken on old libstdc++ as provided with macOS. See +// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html +# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND +#endif + +#ifdef __APPLE__ +// Workaround macOS linker warning: Xcode uses different default symbol +// visibilities for static libs vs executables (see issue #25) +# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) +#else +# define TINYFORMAT_HIDDEN +#endif + +namespace tinyformat { + +//------------------------------------------------------------------------------ +namespace detail { + +// Test whether type T1 is convertible to type T2 +template +struct is_convertible +{ + private: + // two types of different size + struct fail { char dummy[2]; }; + struct succeed { char dummy; }; + // Try to convert a T1 to a T2 by plugging into tryConvert + static fail tryConvert(...); + static succeed tryConvert(const T2&); + static const T1& makeT1(); + public: +# ifdef _MSC_VER + // Disable spurious loss of precision warnings in tryConvert(makeT1()) +# pragma warning(push) +# pragma warning(disable:4244) +# pragma warning(disable:4267) +# endif + // Standard trick: the (...) version of tryConvert will be chosen from + // the overload set only if the version taking a T2 doesn't match. + // Then we compare the sizes of the return types to check which + // function matched. Very neat, in a disgusting kind of way :) + static const bool value = + sizeof(tryConvert(makeT1())) == sizeof(succeed); +# ifdef _MSC_VER +# pragma warning(pop) +# endif +}; + + +// Detect when a type is not a wchar_t string +template struct is_wchar { typedef int tinyformat_wchar_is_not_supported; }; +template<> struct is_wchar {}; +template<> struct is_wchar {}; +template struct is_wchar {}; +template struct is_wchar {}; + + +// Format the value by casting to type fmtT. This default implementation +// should never be called. +template::value> +struct formatValueAsType +{ + static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); } +}; +// Specialized version for types that can actually be converted to fmtT, as +// indicated by the "convertible" template parameter. +template +struct formatValueAsType +{ + static void invoke(std::ostream& out, const T& value) + { out << static_cast(value); } +}; + +#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND +template::value> +struct formatZeroIntegerWorkaround +{ + static bool invoke(std::ostream& /**/, const T& /**/) { return false; } +}; +template +struct formatZeroIntegerWorkaround +{ + static bool invoke(std::ostream& out, const T& value) + { + if (static_cast(value) == 0 && out.flags() & std::ios::showpos) + { + out << "+0"; + return true; + } + return false; + } +}; +#endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND + +// Convert an arbitrary type to integer. The version with convertible=false +// throws an error. +template::value> +struct convertToInt +{ + static int invoke(const T& /*value*/) + { + TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to " + "integer for use as variable width or precision"); + return 0; + } +}; +// Specialization for convertToInt when conversion is possible +template +struct convertToInt +{ + static int invoke(const T& value) { return static_cast(value); } +}; + +// Format at most ntrunc characters to the given stream. +template +inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) +{ + std::ostringstream tmp; + tmp << value; + std::string result = tmp.str(); + out.write(result.c_str(), (std::min)(ntrunc, static_cast(result.size()))); +} +#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \ +inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ +{ \ + std::streamsize len = 0; \ + while(len < ntrunc && value[len] != 0) \ + ++len; \ + out.write(value, len); \ +} +// Overload for const char* and char*. Could overload for signed & unsigned +// char too, but these are technically unneeded for printf compatibility. +TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char) +TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char) +#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR + +} // namespace detail + + +//------------------------------------------------------------------------------ +// Variable formatting functions. May be overridden for user-defined types if +// desired. + + +/// Format a value into a stream, delegating to operator<< by default. +/// +/// Users may override this for their own types. When this function is called, +/// the stream flags will have been modified according to the format string. +/// The format specification is provided in the range [fmtBegin, fmtEnd). For +/// truncating conversions, ntrunc is set to the desired maximum number of +/// characters, for example "%.7s" calls formatValue with ntrunc = 7. +/// +/// By default, formatValue() uses the usual stream insertion operator +/// operator<< to format the type T, with special cases for the %c and %p +/// conversions. +template +inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, + const char* fmtEnd, int ntrunc, const T& value) +{ +#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS + // Since we don't support printing of wchar_t using "%ls", make it fail at + // compile time in preference to printing as a void* at runtime. + typedef typename detail::is_wchar::tinyformat_wchar_is_not_supported DummyType; + (void) DummyType(); // avoid unused type warning with gcc-4.8 +#endif + // The mess here is to support the %c and %p conversions: if these + // conversions are active we try to convert the type to a char or const + // void* respectively and format that instead of the value itself. For the + // %p conversion it's important to avoid dereferencing the pointer, which + // could otherwise lead to a crash when printing a dangling (const char*). + const bool canConvertToChar = detail::is_convertible::value; + const bool canConvertToVoidPtr = detail::is_convertible::value; + if(canConvertToChar && *(fmtEnd-1) == 'c') + detail::formatValueAsType::invoke(out, value); + else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') + detail::formatValueAsType::invoke(out, value); +#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND + else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; +#endif + else if(ntrunc >= 0) + { + // Take care not to overread C strings in truncating conversions like + // "%.4s" where at most 4 characters may be read. + detail::formatTruncated(out, value, ntrunc); + } + else + out << value; +} + + +// Overloaded version for char types to support printing as an integer +#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ +inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ + const char* fmtEnd, int /**/, charType value) \ +{ \ + switch(*(fmtEnd-1)) \ + { \ + case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ + out << static_cast(value); break; \ + default: \ + out << value; break; \ + } \ +} +// per 3.9.1: char, signed char and unsigned char are all distinct types +TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) +TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) +TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) +#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR + + +//------------------------------------------------------------------------------ +// Tools for emulating variadic templates in C++98. The basic idea here is +// stolen from the boost preprocessor metaprogramming library and cut down to +// be just general enough for what we need. + +#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n +#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n +#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n +#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n + +// To keep it as transparent as possible, the macros below have been generated +// using python via the excellent cog.py code generation script. This avoids +// the need for a bunch of complex (but more general) preprocessor tricks as +// used in boost.preprocessor. +// +// To rerun the code generation in place, use `cog.py -r tinyformat.h` +// (see http://nedbatchelder.com/code/cog). Alternatively you can just create +// extra versions by hand. + +/*[[[cog +maxParams = 16 + +def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): + for j in range(startInd,maxParams+1): + list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) + cog.outl(lineTemplate % {'j':j, 'list':list}) + +makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', + 'class T%(i)d') + +cog.outl() +makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', + 'const T%(i)d& v%(i)d') + +cog.outl() +makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') + +cog.outl() +cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') +makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', + 'v%(i)d', startInd = 2) + +cog.outl() +cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + + ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) +]]]*/ +#define TINYFORMAT_ARGTYPES_1 class T1 +#define TINYFORMAT_ARGTYPES_2 class T1, class T2 +#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 +#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 +#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 +#define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6 +#define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7 +#define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 +#define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 +#define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10 +#define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11 +#define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12 +#define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13 +#define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14 +#define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 +#define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16 + +#define TINYFORMAT_VARARGS_1 const T1& v1 +#define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2 +#define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3 +#define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4 +#define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5 +#define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6 +#define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7 +#define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8 +#define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9 +#define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10 +#define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11 +#define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12 +#define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13 +#define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14 +#define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15 +#define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16 + +#define TINYFORMAT_PASSARGS_1 v1 +#define TINYFORMAT_PASSARGS_2 v1, v2 +#define TINYFORMAT_PASSARGS_3 v1, v2, v3 +#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 +#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 +#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 +#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 +#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 +#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 +#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 +#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 +#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 +#define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 +#define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 +#define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 +#define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 + +#define TINYFORMAT_PASSARGS_TAIL_1 +#define TINYFORMAT_PASSARGS_TAIL_2 , v2 +#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 +#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 +#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 +#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 +#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 +#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 +#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 +#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 +#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 +#define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 +#define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 +#define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 +#define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 +#define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 + +#define TINYFORMAT_FOREACH_ARGNUM(m) \ + m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16) +//[[[end]]] + + + +namespace detail { + +// Type-opaque holder for an argument to format(), with associated actions on +// the type held as explicit function pointers. This allows FormatArg's for +// each argument to be allocated as a homogeneous array inside FormatList +// whereas a naive implementation based on inheritance does not. +class FormatArg +{ + public: + FormatArg() + : m_value(NULL), + m_formatImpl(NULL), + m_toIntImpl(NULL) + { } + + template + FormatArg(const T& value) + : m_value(static_cast(&value)), + m_formatImpl(&formatImpl), + m_toIntImpl(&toIntImpl) + { } + + void format(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc) const + { + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_formatImpl); + m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); + } + + int toInt() const + { + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_toIntImpl); + return m_toIntImpl(m_value); + } + + private: + template + TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc, const void* value) + { + formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast(value)); + } + + template + TINYFORMAT_HIDDEN static int toIntImpl(const void* value) + { + return convertToInt::invoke(*static_cast(value)); + } + + const void* m_value; + void (*m_formatImpl)(std::ostream& out, const char* fmtBegin, + const char* fmtEnd, int ntrunc, const void* value); + int (*m_toIntImpl)(const void* value); +}; + + +// Parse and return an integer from the string c, as atoi() +// On return, c is set to one past the end of the integer. +inline int parseIntAndAdvance(const char*& c) +{ + int i = 0; + for(;*c >= '0' && *c <= '9'; ++c) + i = 10*i + (*c - '0'); + return i; +} + +// Parse width or precision `n` from format string pointer `c`, and advance it +// to the next character. If an indirection is requested with `*`, the argument +// is read from `formatters[argIndex]` and `argIndex` is incremented (or read +// from `formatters[n]` in positional mode). Returns true if one or more +// characters were read. +inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode, + const detail::FormatArg* formatters, + int& argIndex, int numFormatters) +{ + if(*c >= '0' && *c <= '9') + { + n = parseIntAndAdvance(c); + } + else if(*c == '*') + { + ++c; + n = 0; + if(positionalMode) + { + int pos = parseIntAndAdvance(c) - 1; + if(*c != '$') + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + if(pos >= 0 && pos < numFormatters) + n = formatters[pos].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Positional argument out of range"); + ++c; + } + else + { + if(argIndex < numFormatters) + n = formatters[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision"); + } + } + else + { + return false; + } + return true; +} + +// Print literal part of format string and return next format spec +// position. +// +// Skips over any occurrences of '%%', printing a literal '%' to the +// output. The position of the first % character of the next +// nontrivial format spec is returned, or the end of string. +inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) +{ + const char* c = fmt; + for(;; ++c) + { + switch(*c) + { + case '\0': + out.write(fmt, c - fmt); + return c; + case '%': + out.write(fmt, c - fmt); + if(*(c+1) != '%') + return c; + // for "%%", tack trailing % onto next literal section. + fmt = ++c; + break; + default: + break; + } + } +} + + +// Parse a format string and set the stream state accordingly. +// +// The format mini-language recognized here is meant to be the one from C99, +// with the form "%[flags][width][.precision][length]type" with POSIX +// positional arguments extension. +// +// POSIX positional arguments extension: +// Conversions can be applied to the nth argument after the format in +// the argument list, rather than to the next unused argument. In this case, +// the conversion specifier character % (see below) is replaced by the sequence +// "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}], +// giving the position of the argument in the argument list. This feature +// provides for the definition of format strings that select arguments +// in an order appropriate to specific languages. +// +// The format can contain either numbered argument conversion specifications +// (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications +// (that is, % and * ), but not both. The only exception to this is that %% +// can be mixed with the "%n$" form. The results of mixing numbered and +// unnumbered argument specifications in a format string are undefined. +// When numbered argument specifications are used, specifying the Nth argument +// requires that all the leading arguments, from the first to the (N-1)th, +// are specified in the format string. +// +// In format strings containing the "%n$" form of conversion specification, +// numbered arguments in the argument list can be referenced from the format +// string as many times as required. +// +// Formatting options which can't be natively represented using the ostream +// state are returned in spacePadPositive (for space padded positive numbers) +// and ntrunc (for truncating conversions). argIndex is incremented if +// necessary to pull out variable width and precision. The function returns a +// pointer to the character after the end of the current format spec. +inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode, + bool& spacePadPositive, + int& ntrunc, const char* fmtStart, + const detail::FormatArg* formatters, + int& argIndex, int numFormatters) +{ + if(*fmtStart != '%') + { + TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); + return fmtStart; + } + // Reset stream state to defaults. + out.width(0); + out.precision(6); + out.fill(' '); + // Reset most flags; ignore irrelevant unitbuf & skipws. + out.unsetf(std::ios::adjustfield | std::ios::basefield | + std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | + std::ios::showpoint | std::ios::showpos | std::ios::uppercase); + bool precisionSet = false; + bool widthSet = false; + int widthExtra = 0; + const char* c = fmtStart + 1; + + // 1) Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag. + if(*c >= '0' && *c <= '9') + { + const char tmpc = *c; + int value = parseIntAndAdvance(c); + if(*c == '$') + { + // value is an argument index + if(value > 0 && value <= numFormatters) + argIndex = value - 1; + else + TINYFORMAT_ERROR("tinyformat: Positional argument out of range"); + ++c; + positionalMode = true; + } + else if(positionalMode) + { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + } + else + { + if(tmpc == '0') + { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + if(value != 0) + { + // Nonzero value means that we parsed width. + widthSet = true; + out.width(value); + } + } + } + else if(positionalMode) + { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + } + // 2) Parse flags and width if we did not do it in previous step. + if(!widthSet) + { + // Parse flags + for(;; ++c) + { + switch(*c) + { + case '#': + out.setf(std::ios::showpoint | std::ios::showbase); + continue; + case '0': + // overridden by left alignment ('-' flag) + if(!(out.flags() & std::ios::left)) + { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + continue; + case '-': + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + continue; + case ' ': + // overridden by show positive sign, '+' flag. + if(!(out.flags() & std::ios::showpos)) + spacePadPositive = true; + continue; + case '+': + out.setf(std::ios::showpos); + spacePadPositive = false; + widthExtra = 1; + continue; + default: + break; + } + break; + } + // Parse width + int width = 0; + widthSet = parseWidthOrPrecision(width, c, positionalMode, + formatters, argIndex, numFormatters); + if(widthSet) + { + if(width < 0) + { + // negative widths correspond to '-' flag set + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + width = -width; + } + out.width(width); + } + } + // 3) Parse precision + if(*c == '.') + { + ++c; + int precision = 0; + parseWidthOrPrecision(precision, c, positionalMode, + formatters, argIndex, numFormatters); + // Presence of `.` indicates precision set, unless the inferred value + // was negative in which case the default is used. + precisionSet = precision >= 0; + if(precisionSet) + out.precision(precision); + } + // 4) Ignore any C99 length modifier + while(*c == 'l' || *c == 'h' || *c == 'L' || + *c == 'j' || *c == 'z' || *c == 't') + ++c; + // 5) We're up to the conversion specifier character. + // Set stream flags based on conversion specifier (thanks to the + // boost::format class for forging the way here). + bool intConversion = false; + switch(*c) + { + case 'u': case 'd': case 'i': + out.setf(std::ios::dec, std::ios::basefield); + intConversion = true; + break; + case 'o': + out.setf(std::ios::oct, std::ios::basefield); + intConversion = true; + break; + case 'X': + out.setf(std::ios::uppercase); + // Falls through + case 'x': case 'p': + out.setf(std::ios::hex, std::ios::basefield); + intConversion = true; + break; + case 'E': + out.setf(std::ios::uppercase); + // Falls through + case 'e': + out.setf(std::ios::scientific, std::ios::floatfield); + out.setf(std::ios::dec, std::ios::basefield); + break; + case 'F': + out.setf(std::ios::uppercase); + // Falls through + case 'f': + out.setf(std::ios::fixed, std::ios::floatfield); + break; + case 'A': + out.setf(std::ios::uppercase); + // Falls through + case 'a': +# ifdef _MSC_VER + // Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html + // by always setting maximum precision on MSVC to avoid precision + // loss for doubles. + out.precision(13); +# endif + out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield); + break; + case 'G': + out.setf(std::ios::uppercase); + // Falls through + case 'g': + out.setf(std::ios::dec, std::ios::basefield); + // As in boost::format, let stream decide float format. + out.flags(out.flags() & ~std::ios::floatfield); + break; + case 'c': + // Handled as special case inside formatValue() + break; + case 's': + if(precisionSet) + ntrunc = static_cast(out.precision()); + // Make %s print Booleans as "true" and "false" + out.setf(std::ios::boolalpha); + break; + case 'n': + // Not supported - will cause problems! + TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); + break; + case '\0': + TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " + "terminated by end of string"); + return c; + default: + break; + } + if(intConversion && precisionSet && !widthSet) + { + // "precision" for integers gives the minimum number of digits (to be + // padded with zeros on the left). This isn't really supported by the + // iostreams, but we can approximately simulate it with the width if + // the width isn't otherwise used. + out.width(out.precision() + widthExtra); + out.setf(std::ios::internal, std::ios::adjustfield); + out.fill('0'); + } + return c+1; +} + + +//------------------------------------------------------------------------------ +inline void formatImpl(std::ostream& out, const char* fmt, + const detail::FormatArg* formatters, + int numFormatters) +{ + // Saved stream state + std::streamsize origWidth = out.width(); + std::streamsize origPrecision = out.precision(); + std::ios::fmtflags origFlags = out.flags(); + char origFill = out.fill(); + + bool positionalMode = false; + for(int argIndex = 0; positionalMode || argIndex < numFormatters; ++argIndex) + { + // Parse the format string + fmt = printFormatStringLiteral(out, fmt); + if(positionalMode && *fmt == '\0') + break; + bool spacePadPositive = false; + int ntrunc = -1; + const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt, + formatters, argIndex, numFormatters); + if (argIndex >= numFormatters) + { + // Check args remain after reading any variable width/precision + TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); + return; + } + const FormatArg& arg = formatters[argIndex]; + // Format the arg into the stream. + if(!spacePadPositive) + arg.format(out, fmt, fmtEnd, ntrunc); + else + { + // The following is a special case with no direct correspondence + // between stream formatting and the printf() behaviour. Simulate + // it crudely by formatting into a temporary string stream and + // munging the resulting string. + std::ostringstream tmpStream; + tmpStream.copyfmt(out); + tmpStream.setf(std::ios::showpos); + arg.format(tmpStream, fmt, fmtEnd, ntrunc); + std::string result = tmpStream.str(); // allocates... yuck. + for(size_t i = 0, iend = result.size(); i < iend; ++i) + if(result[i] == '+') result[i] = ' '; + out << result; + } + fmt = fmtEnd; + } + + // Print remaining part of format string. + fmt = printFormatStringLiteral(out, fmt); + if(*fmt != '\0') + TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); + + // Restore stream state + out.width(origWidth); + out.precision(origPrecision); + out.flags(origFlags); + out.fill(origFill); +} + +} // namespace detail + + +/// List of template arguments format(), held in a type-opaque way. +/// +/// A const reference to FormatList (typedef'd as FormatListRef) may be +/// conveniently used to pass arguments to non-template functions: All type +/// information has been stripped from the arguments, leaving just enough of a +/// common interface to perform formatting as required. +class FormatList +{ + public: + FormatList(detail::FormatArg* formatters, int N) + : m_formatters(formatters), m_N(N) { } + + friend void vformat(std::ostream& out, const char* fmt, + const FormatList& list); + + private: + const detail::FormatArg* m_formatters; + int m_N; +}; + +/// Reference to type-opaque format list for passing to vformat() +typedef const FormatList& FormatListRef; + + +namespace detail { + +// Format list subclass with fixed storage to avoid dynamic allocation +template +class FormatListN : public FormatList +{ + public: +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + template + FormatListN(const Args&... args) + : FormatList(&m_formatterStore[0], N), + m_formatterStore { FormatArg(args)... } + { static_assert(sizeof...(args) == N, "Number of args must be N"); } +#else // C++98 version + void init(int) {} +# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ + \ + template \ + FormatListN(TINYFORMAT_VARARGS(n)) \ + : FormatList(&m_formatterStore[0], n) \ + { TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ + \ + template \ + void init(int i, TINYFORMAT_VARARGS(n)) \ + { \ + m_formatterStore[i] = FormatArg(v1); \ + init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ + } + + TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) +# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR +#endif + + private: + FormatArg m_formatterStore[N]; +}; + +// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard +template<> class FormatListN<0> : public FormatList +{ + public: FormatListN() : FormatList(0, 0) {} +}; + +} // namespace detail + + +//------------------------------------------------------------------------------ +// Primary API functions + +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + +/// Make type-agnostic format list from list of template arguments. +/// +/// The exact return type of this function is an implementation detail and +/// shouldn't be relied upon. Instead it should be stored as a FormatListRef: +/// +/// FormatListRef formatList = makeFormatList( /*...*/ ); +template +detail::FormatListN makeFormatList(const Args&... args) +{ + return detail::FormatListN(args...); +} + +#else // C++98 version + +inline detail::FormatListN<0> makeFormatList() +{ + return detail::FormatListN<0>(); +} +#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \ +template \ +detail::FormatListN makeFormatList(TINYFORMAT_VARARGS(n)) \ +{ \ + return detail::FormatListN(TINYFORMAT_PASSARGS(n)); \ +} +TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) +#undef TINYFORMAT_MAKE_MAKEFORMATLIST + +#endif + +/// Format list of arguments to the stream according to the given format string. +/// +/// The name vformat() is chosen for the semantic similarity to vprintf(): the +/// list of format arguments is held in a single function argument. +inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) +{ + detail::formatImpl(out, fmt, list.m_formatters, list.m_N); +} + + +#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES + +/// Format list of arguments to the stream according to given format string. +template +void format(std::ostream& out, const char* fmt, const Args&... args) +{ + vformat(out, fmt, makeFormatList(args...)); +} + +/// Format list of arguments according to the given format string and return +/// the result as a string. +template +std::string format(const char* fmt, const Args&... args) +{ + std::ostringstream oss; + format(oss, fmt, args...); + return oss.str(); +} + +/// Format list of arguments to std::cout, according to the given format string +template +void printf(const char* fmt, const Args&... args) +{ + format(std::cout, fmt, args...); +} + +template +void printfln(const char* fmt, const Args&... args) +{ + format(std::cout, fmt, args...); + std::cout << '\n'; +} + + +#else // C++98 version + +inline void format(std::ostream& out, const char* fmt) +{ + vformat(out, fmt, makeFormatList()); +} + +inline std::string format(const char* fmt) +{ + std::ostringstream oss; + format(oss, fmt); + return oss.str(); +} + +inline void printf(const char* fmt) +{ + format(std::cout, fmt); +} + +inline void printfln(const char* fmt) +{ + format(std::cout, fmt); + std::cout << '\n'; +} + +#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ + \ +template \ +void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \ +} \ + \ +template \ +std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + std::ostringstream oss; \ + format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ + return oss.str(); \ +} \ + \ +template \ +void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ +} \ + \ +template \ +void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ + std::cout << '\n'; \ +} + +TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) +#undef TINYFORMAT_MAKE_FORMAT_FUNCS + +#endif + + +} // namespace tinyformat + +#endif // TINYFORMAT_H_INCLUDED diff --git a/hilti/include/3rdparty/tinyformat/tinyformat_test.cpp b/hilti/include/3rdparty/tinyformat/tinyformat_test.cpp new file mode 100644 index 000000000..e0e7882ae --- /dev/null +++ b/hilti/include/3rdparty/tinyformat/tinyformat_test.cpp @@ -0,0 +1,255 @@ +#if defined(__linux__) && defined(__clang__) +// Workaround for bug in gcc 4.4 standard library headers when compling with +// clang in C++11 mode. +namespace std { class type_info; } +#endif + +#include +#include +#include +#include + +#ifdef SPEED_TEST +#include +#include +#include +#endif + + +// Throw instead of abort() so we can test error conditions. +#define TINYFORMAT_ERROR(reason) \ + throw std::runtime_error(reason); + +#include "tinyformat.h" +#include + +#if 0 +// Compare result of tfm::format() to C's sprintf(). +template +void compareSprintf(const Args&... args) +{ + std::string tfmResult = tfm::format(args...); + char sprintfResult[200]; + sprintf(sprintfResult, args...); + if(tfmResult != sprintfResult) + { + std::cout << tfmResult << std::endl; + std::cout << sprintfResult << std::endl; + assert(0 && "results didn't match, see above."); + } +} +#endif + +#define EXPECT_ERROR(expression) \ +{ \ + try { expression; assert(0 && "expected exception"); } \ + catch(std::runtime_error&) {} \ +} + +#define CHECK_EQUAL(a, b) \ +if(!((a) == (b))) \ +{ \ + std::cout << "test failed, line " << __LINE__ << "\n"; \ + std::cout << (a) << " != " << (b) << "\n"; \ + std::cout << "[" #a ", " #b "]\n"; \ + ++nfailed; \ +} + + +// Test wrapping to create our own function which calls through to tfm::format +struct TestWrap +{ + std::ostringstream m_oss; +# undef TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS +# define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS int code, + // std::string error(int code, const char* fmt, const Args&... args); + TINYFORMAT_WRAP_FORMAT(std::string, error, /**/, + m_oss.clear(); m_oss << code << ": ";, + m_oss, + return m_oss.str();) +# undef TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS +# define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS +}; + + +int unitTests() +{ + int nfailed = 0; +# ifdef _MSC_VER + // floats are printed with three digit exponents on windows, which messes + // up the tests. Turn this off for consistency: + _set_output_format(_TWO_DIGIT_EXPONENT); +# endif + // Test various basic format specs against results of sprintf + CHECK_EQUAL(tfm::format("%s", "asdf"), "asdf"); + CHECK_EQUAL(tfm::format("%d", 1234), "1234"); + CHECK_EQUAL(tfm::format("%i", -5678), "-5678"); + CHECK_EQUAL(tfm::format("%o", 012), "12"); + CHECK_EQUAL(tfm::format("%u", 123456u), "123456"); + CHECK_EQUAL(tfm::format("%x", 0xdeadbeef), "deadbeef"); + CHECK_EQUAL(tfm::format("%X", 0xDEADBEEF), "DEADBEEF"); + CHECK_EQUAL(tfm::format("%e", 1.23456e10), "1.234560e+10"); + CHECK_EQUAL(tfm::format("%E", -1.23456E10), "-1.234560E+10"); + CHECK_EQUAL(tfm::format("%f", -9.8765), "-9.876500"); + CHECK_EQUAL(tfm::format("%F", 9.8765), "9.876500"); + CHECK_EQUAL(tfm::format("%g", 10), "10"); + CHECK_EQUAL(tfm::format("%G", 100), "100"); + CHECK_EQUAL(tfm::format("%c", 65), "A"); + CHECK_EQUAL(tfm::format("%hc", (short)65), "A"); + CHECK_EQUAL(tfm::format("%lc", (long)65), "A"); + CHECK_EQUAL(tfm::format("%s", "asdf_123098"), "asdf_123098"); + // Note: All tests printing pointers are different on windows, since + // there's no standard numerical representation. +# ifdef _MSC_VER + CHECK_EQUAL(tfm::format("%p", (void*)0x12345), "00012345"); +# else + CHECK_EQUAL(tfm::format("%p", (void*)0x12345), "0x12345"); +# endif + CHECK_EQUAL(tfm::format("%%%s", "asdf"), "%asdf"); // note: plain "%%" format gives warning with gcc + // chars with int format specs are printed as ints: + CHECK_EQUAL(tfm::format("%hhd", (char)65), "65"); + CHECK_EQUAL(tfm::format("%hhu", (unsigned char)65), "65"); + CHECK_EQUAL(tfm::format("%hhd", (signed char)65), "65"); +# ifdef _MSC_VER + CHECK_EQUAL(tfm::format("%p", (const char*)0x10), "00000010"); +# else + CHECK_EQUAL(tfm::format("%p", (const char*)0x10), "0x10"); // should print address, not string. +# endif + // bools with string format spec are printed as "true" or "false" + CHECK_EQUAL(tfm::format("%s", true), "true"); + CHECK_EQUAL(tfm::format("%d", true), "1"); + + // Test precision & width + CHECK_EQUAL(tfm::format("%10d", -10), " -10"); + CHECK_EQUAL(tfm::format("%.4d", 10), "0010"); + CHECK_EQUAL(tfm::format("%10.4f", 1234.1234567890), " 1234.1235"); + CHECK_EQUAL(tfm::format("%.f", 10.1), "10"); + CHECK_EQUAL(tfm::format("%.2s", "asdf"), "as"); // strings truncate to precision +// // Test variable precision & width + CHECK_EQUAL(tfm::format("%*.4f", 10, 1234.1234567890), " 1234.1235"); + CHECK_EQUAL(tfm::format("%10.*f", 4, 1234.1234567890), " 1234.1235"); + CHECK_EQUAL(tfm::format("%*.*f", 10, 4, 1234.1234567890), " 1234.1235"); + CHECK_EQUAL(tfm::format("%*.*f", -10, 4, 1234.1234567890), "1234.1235 "); + + // Test flags + CHECK_EQUAL(tfm::format("%#x", 0x271828), "0x271828"); + CHECK_EQUAL(tfm::format("%#o", 0x271828), "011614050"); + CHECK_EQUAL(tfm::format("%#f", 3.0), "3.000000"); + CHECK_EQUAL(tfm::format("%010d", 100), "0000000100"); + CHECK_EQUAL(tfm::format("%010d", -10), "-000000010"); // sign should extend + CHECK_EQUAL(tfm::format("%#010X", 0xBEEF), "0X0000BEEF"); + // flag override precedence + CHECK_EQUAL(tfm::format("%+ 10d", 10), " +10"); // '+' overrides ' ' + CHECK_EQUAL(tfm::format("% +10d", 10), " +10"); + CHECK_EQUAL(tfm::format("%-010d", 10), "10 "); // '-' overrides '0' + CHECK_EQUAL(tfm::format("%0-10d", 10), "10 "); + + // Check that length modifiers are ignored + CHECK_EQUAL(tfm::format("%hd", (short)1000), "1000"); + CHECK_EQUAL(tfm::format("%ld", (long)100000), "100000"); + CHECK_EQUAL(tfm::format("%lld", (long long)100000), "100000"); + CHECK_EQUAL(tfm::format("%zd", (size_t)100000), "100000"); + CHECK_EQUAL(tfm::format("%td", (ptrdiff_t)100000), "100000"); + CHECK_EQUAL(tfm::format("%jd", 100000), "100000"); + + // printf incompatibilities: + // compareSprintf("%6.4x", 10); // precision & width can't be supported independently + // compareSprintf("%.4d", -10); // negative numbers + precision don't quite work. + + // General "complicated" format spec test + CHECK_EQUAL(tfm::format("%0.10f:%04d:%+g:%s:%#X:%c:%%:%%asdf", + 1.234, 42, 3.13, "str", 0XDEAD, (int)'X'), + "1.2340000000:0042:+3.13:str:0XDEAD:X:%:%asdf"); + // Test wrong number of args + EXPECT_ERROR( + tfm::format("%d", 5, 10) + ) + EXPECT_ERROR( + tfm::format("%d") + ) + // Unterminated format spec + EXPECT_ERROR( + tfm::format("%123", 10) + ) + // Types used to specify variable width/precision must be convertible to int. + EXPECT_ERROR( + tfm::format("%0*d", "thing that can't convert to int", 42) + ) + EXPECT_ERROR( + tfm::format("%0.*d", "thing that can't convert to int", 42) + ) + + // Test that formatting is independent of underlying stream state. + std::ostringstream oss; + oss.width(20); + oss.precision(10); + oss.fill('*'); + oss.setf(std::ios::scientific); + tfm::format(oss, "%f", 10.1234123412341234); + assert(oss.str() == "10.123412"); + + // Test that interface wrapping works correctly + TestWrap wrap; + assert(wrap.error(10, "someformat %s:%d:%d", "asdf", 2, 4) == + "10: someformat asdf:2:4"); + return nfailed; +} + + +#ifdef SPEED_TEST +void speedTest(const std::string& which) +{ + // Following is required so that we're not limited by per-character + // buffering. + std::ios_base::sync_with_stdio(false); + const long maxIter = 2000000L; + if(which == "printf") + { + // libc version + for(long i = 0; i < maxIter; ++i) + printf("%0.10f:%04d:%+g:%s:%p:%c:%%\n", + 1.234, 42, 3.13, "str", (void*)1000, (int)'X'); + } + else if(which == "iostreams") + { + // Std iostreams version. What a mess!! + for(long i = 0; i < maxIter; ++i) + std::cout << std::setprecision(10) << 1.234 << ":" + << std::setw(4) << std::setfill('0') << 42 << std::setfill(' ') << ":" + << std::setiosflags(std::ios::showpos) << 3.13 << std::resetiosflags(std::ios::showpos) << ":" + << "str" << ":" + << (void*)1000 << ":" + << (int)'X' << ":%\n"; + } + else if(which == "tinyformat") + { + // tinyformat version. + for(long i = 0; i < maxIter; ++i) + tfm::printf("%0.10f:%04d:%+g:%s:%p:%c:%%\n", + 1.234, 42, 3.13, "str", (void*)1000, (int)'X'); + } + else if(which == "boost") + { + // boost::format version + for(long i = 0; i < maxIter; ++i) + std::cout << boost::format("%0.10f:%04d:%+g:%s:%p:%c:%%\n") + % 1.234 % 42 % 3.13 % "str" % (void*)1000 % (int)'X'; + } + else + { + assert(0 && "speed test for which version?"); + } +} +#endif + + +int main(int argc, char* argv[]) +{ +#ifdef SPEED_TEST + if(argc >= 2) + speedTest(argv[1]); + return 0; +#else + return unitTests(); +#endif +} diff --git a/hilti/include/3rdparty/utf8proc b/hilti/include/3rdparty/utf8proc new file mode 120000 index 000000000..6a03c2e7e --- /dev/null +++ b/hilti/include/3rdparty/utf8proc @@ -0,0 +1 @@ +../../src/3rdparty/utf8proc/ \ No newline at end of file diff --git a/hilti/include/ast/all.h b/hilti/include/ast/all.h new file mode 100644 index 000000000..c8ea470f4 --- /dev/null +++ b/hilti/include/ast/all.h @@ -0,0 +1,10 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/ast.h b/hilti/include/ast/ast.h new file mode 100644 index 000000000..bc872d4f4 --- /dev/null +++ b/hilti/include/ast/ast.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +/** Forwards node construction to a suitable type `T`. */ +template +static Node to_node(Params&&... params) { + // Must come after all other includes so that all the to_node() are available. + return to_node(T(std::forward(params)...)); +} +} // namespace hilti diff --git a/hilti/include/ast/attribute.h b/hilti/include/ast/attribute.h new file mode 100644 index 000000000..7d3338f8b --- /dev/null +++ b/hilti/include/ast/attribute.h @@ -0,0 +1,284 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +/** AST node captures an `&` attribute along with an optional argument. */ +class Attribute : public NodeBase { +public: + Attribute() = default; + + /** + * Constructor for an attribute with no argument. + * + * @param tag name of the attribute, including the leading `&` + * @param m meta data to associate with the node + */ + Attribute(std::string tag, Meta m = Meta()) : NodeBase({node::none}, std::move(m)), _tag(std::move(tag)) {} + + /** + * Constructor for an attribute coming with an argument. The argument + * must be either an AST node representing an expression. + * + * @param tag name of the attribute, including the leading `&` + * @param v node representing the argument to associate with the attribute; must be an expression + * @param m meta data to associate with the node + */ + Attribute(std::string tag, Node v, Meta m = Meta()) + : NodeBase({std::move(v)}, std::move(m)), _tag(std::move(tag)) {} + + /** Returns the name of the attribute, including the leading `&`. */ + auto tag() const { return _tag; } + + /** Returns true if an argument is associated with the attribute. */ + bool hasValue() const { return ! childs()[0].isA(); } + + /** + * Returns the attribute associated with the node. + * + * @exception `std::out_of_range` if the attribute does not have an argument + */ + const Node& value() const { return childs().at(0); } + + /** + * Returns the attributes argument as type `T`. `T` must be either an + * `Expression`, or `std::string`. In the former case, the value must be + * an AST expression node. In the latter case, the argument must be a + * string constructor expression, and the returned value will be the + * string it represents. + * + * @tparam T either `Expression` or `std::string` + * @return the argument, or an error if the argument could not be interpreted as type `T` + * @exception `std::out_of_range` if the attribute does not have an argument + */ + template + Result valueAs() const { + if constexpr ( std::is_same::value ) { + if ( auto e = value().tryAs() ) + return *e; + + return result::Error(util::fmt("value for attribute '%s' must be an expression", _tag)); + } + + if constexpr ( std::is_same::value ) { + if ( auto e = value().tryAs() ) + if ( auto s = e->ctor().tryAs() ) + return s->value(); + return result::Error(util::fmt("value for attribute '%s' must be a string", _tag)); + } + + logger().internalError(util::fmt("unsupported attribute value type requested (%s)", typeid(T).name())); + } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"tag", _tag}}; } + + bool operator==(const Attribute& other) const { + if ( _tag != other._tag ) + return false; + + if ( auto x = valueAs() ) { + auto y = other.valueAs(); + return y && *x == *y; + } + + if ( auto x = valueAs() ) { + auto y = other.valueAs(); + return y && *x == *y; + } + + return false; + } + + /** + * Sets or replaces an attributes's associated argument. + * + * @param the node to assign as the attribute's argument + * @return a new attribute with the value changed + */ + static Attribute setValue(const Attribute& a, Node n) { + auto x = Attribute(a); + if ( a.childs().empty() ) + x.childs().emplace_back(std::move(n)); + else + x.childs()[0] = std::move(n); + + return x; + } + +private: + std::string _tag; +}; + +/** + * Constructs an AST node from an attribute. + */ +inline Node to_node(Attribute i) { return Node(std::move(i)); } + +/** AST node holding a set of `Attribute` nodes. */ +class AttributeSet : public NodeBase { +public: + /** + * Constructs a set from from a vector of attributes. + * + * @param a vector to initialize attribute set from + * @param m meta data to associate with the node + */ + explicit AttributeSet(std::vector a, Meta m = Meta()) : NodeBase(nodes(std::move(a)), std::move(m)) {} + + /** Returns the set's attributes. */ + std::vector attributes() const { return childs(0, -1); } + + /** + * Retrieves an attribute with a given name from the set. If multiple + * attributes with that tag exist, it's undefined which is returned. + * + * @return attribute if found + */ + std::optional find(std::string_view tag) const { + for ( auto& a : attributes() ) + if ( a.tag() == tag ) + return a; + + return {}; + } + + /** + * If an attribute of a given name exists and has an expression value, + * the value is coerced to a specified type. If not, nothing is done. + * + * @return A successful return value if either the coercion succeeded + * (then the result's value is true), or nothing was to be done (then the + * result's value is false); a failures if a coercionk would have been + * necessary, but failed. + */ + Result coerceValueTo(const std::string& tag, const Type& dst) { + for ( auto& n : childs() ) { + auto a = n.as(); + if ( a.tag() != tag ) + continue; + + if ( auto e = a.valueAs() ) { + auto ne = coerceExpression(*e, dst); + if ( ! ne.coerced ) + return result::Error("cannot coerce attribute value"); + + if ( ne.nexpr ) { + n = Attribute(tag, std::move(*ne.nexpr)); + return true; + } + + return false; + } + } + + return false; + } + + /** + * Returns true if there's an attribute with a given name in the set. + * + * @param true if found + */ + bool has(std::string_view tag) const { return find(tag).has_value(); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const AttributeSet& other) const { return attributes() == other.attributes(); }; + + /** Returns true if the set has at least one element. */ + operator bool() const { return ! childs().empty(); } + + /** + * Returns a new attribute set that adds one element. + * + * @param s set to add to. + * @param a element to add. + * @return `s` with `a' added + */ + static AttributeSet add(AttributeSet s, Attribute a) { + s.addChild(std::move(a)); + return s; + } + + /** + * Returns a new attribute set that adds one element. + * + * @param s set to add to. + * @param a element to add. + * @return `s` with `a' added + */ + static std::optional add(std::optional s, Attribute a) { + if ( ! s ) + s = AttributeSet({}, a.meta()); + + s->addChild(std::move(a)); + return s; + } + + /** + * Retrieves an attribute with a given name from a set, dealing correctly + * with an unset optional set. If multiple attributes with that tag + * exist, it's undefined which is returned. + * + * @param attrs set to inspect + * @return attribute if found + */ + static std::optional find(const std::optional& attrs, std::string_view tag) { + if ( attrs ) + return attrs->find(tag); + else + return {}; + } + + /** + * Returns true if there's an attribute with a given name in a set, + * dealing correctly with an unset optional set. + * + * @param attrs set to inspect + * @param true if found + */ + static bool has(const std::optional& attrs, std::string_view tag) { + if ( attrs ) + return attrs->has(tag); + else + return false; + } + +private: + /** + * Constructs an empty set. + * + * @note We make this private so that it's harder to create an empty set. + * Usually we pass them around as optionals. + * + * @param m meta data to associate with the node + */ + AttributeSet(Meta m = Meta()) : NodeBase({}, std::move(m)) {} +}; + +/** + * Constructs an AST node from an attribute set. + */ +inline Node to_node(AttributeSet i) { return Node(std::move(i)); } +inline Node to_node(std::optional&& i) { return i ? to_node(*i) : node::none; } + +} // namespace hilti diff --git a/hilti/include/ast/builder/all.h b/hilti/include/ast/builder/all.h new file mode 100644 index 000000000..f9b56ffb6 --- /dev/null +++ b/hilti/include/ast/builder/all.h @@ -0,0 +1,9 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +#include diff --git a/hilti/include/ast/builder/builder.h b/hilti/include/ast/builder/builder.h new file mode 100644 index 000000000..bd49a65a9 --- /dev/null +++ b/hilti/include/ast/builder/builder.h @@ -0,0 +1,253 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti { +class Context; +} // namespace hilti + +namespace hilti::builder { + +class Builder { +public: + Builder(std::shared_ptr context) + : _context(std::move(context)), _our_block(statement::Block()), _block(*_our_block) {} + + Statement block() { + assert(_our_block); + return *_our_block; + } + + Expression addTmp(const std::string& prefix, const Expression& init); + Expression addTmp(const std::string& prefix, const Type& t); + Expression addTmp(const std::string& prefix, const Type& t, const Expression& init); + + void addLocal(ID id, Type t, Meta m = Meta()) { + _block._add(builder::local(std::move(id), std::move(t), std::move(m))); + } + + void addLocal(ID id, Expression init, Meta m = Meta()) { + _block._add(builder::local(std::move(id), std::move(init), std::move(m))); + } + + void addLocal(ID id, Type t, Expression init, Meta m = Meta()) { + _block._add(builder::local(std::move(id), std::move(t), std::move(init), std::move(m))); + } + + void addLocal(ID id, Type t, std::vector args, Meta m = Meta()) { + _block._add(builder::local(std::move(id), std::move(t), std::move(args), std::move(m))); + } + + void addExpression(const Expression& expr) { _block._add(statement::Expression(expr, expr.meta())); } + + void addAssert(Expression cond, std::string msg, Meta m = Meta()) { + _block._add(statement::Assert(std::move(cond), builder::string(std::move(msg)), std::move(m))); + } + + void addAssign(Expression dst, Expression src, const Meta& m = Meta()) { + _block._add(statement::Expression(builder::assign(std::move(dst), std::move(src), m), m)); + } + + void addSumAssign(Expression dst, Expression src, const Meta& m = Meta()) { + _block._add(statement::Expression(builder::sumAssign(std::move(dst), std::move(src), m), m)); + } + + void addAssign(ID dst, Expression src, const Meta& m = Meta()) { + _block._add(statement::Expression(builder::assign(builder::id(std::move(dst)), std::move(src), m), m)); + } + + void addBreak(Meta m = Meta()) { _block._add(statement::Break(std::move(m))); } + + void addContinue(Meta m = Meta()) { _block._add(statement::Continue(std::move(m))); } + + void addSumAssign(ID dst, Expression src, const Meta& m = Meta()) { + _block._add(statement::Expression(builder::sumAssign(builder::id(std::move(dst)), std::move(src), m), m)); + } + + void addCall(ID id, std::vector v, const Meta& m = Meta()) { + _block._add(statement::Expression(builder::call(std::move(id), std::move(v), m), m)); + } + + void addMemberCall(Expression self, const ID& id, std::vector v, const Meta& m = Meta()) { + _block._add(statement::Expression(builder::memberCall(std::move(self), id, std::move(v), m), m)); + } + + void addComment(std::string comment, + hilti::statement::comment::Separator separator = hilti::statement::comment::Separator::Before, + const Meta& m = Meta()) { + comment = util::replace(comment, "\n", ""); + _block._add(statement::Comment(std::move(comment), separator, m)); + } + + void addReturn(Expression e, Meta m = Meta()) { _block._add(statement::Return(std::move(e), std::move(m))); } + + void addReturn(Ctor c, const Meta& m = Meta()) { + _block._add(statement::Return(expression::Ctor(std::move(c), m), m)); + } + + void addReturn(Meta m = Meta()) { _block._add(statement::Return(std::move(m))); } + + void addThrow(Expression excpt, Meta m = Meta()) { _block._add(statement::Throw(std::move(excpt), std::move(m))); } + void addRethrow(Meta m = Meta()) { _block._add(statement::Throw(std::move(m))); } + + void addDebugMsg(const std::string& stream, const std::string& fmt, std::vector args = {}); + void addDebugIndent(const std::string& stream); + void addDebugDedent(const std::string& stream); + + void addPrint(const std::vector& exprs) { addCall("hilti::print", exprs); } + void addPrint(const Expression& expr) { addCall("hilti::print", {expr}); } + + auto addWhile(const statement::Declaration& init, Expression cond, Meta m = Meta()) { + _block._add(statement::While(init.declaration(), std::move(cond), statement::Block(), {}, std::move(m))); + return newBuilder(lastStatement()._bodyNode()); + } + + auto addWhile(Expression cond, Meta m = Meta()) { + _block._add(statement::While(std::move(cond), statement::Block(), {}, std::move(m))); + return newBuilder(lastStatement()._bodyNode()); + } + + auto addWhileElse(const statement::Declaration& init, Expression cond, Meta m = Meta()) { + _block._add(statement::While(init.declaration(), std::move(cond), statement::Block(), statement::Block(), + std::move(m))); + return std::make_pair(newBuilder(lastStatement()._bodyNode()), + newBuilder(lastStatement()._elseNode())); + } + + auto addWhileElse(Expression cond, Meta m = Meta()) { + _block._add(statement::While(std::move(cond), statement::Block(), statement::Block(), std::move(m))); + return std::make_pair(newBuilder(lastStatement()._bodyNode()), + newBuilder(lastStatement()._elseNode())); + } + + auto addIf(const statement::Declaration& init, Expression cond, Meta m = Meta()) { + _block._add(statement::If(init.declaration(), std::move(cond), statement::Block(), {}, std::move(m))); + return newBuilder(lastStatement()._trueNode()); + } + + auto addIf(const statement::Declaration& init, Meta m = Meta()) { + _block._add(statement::If(init.declaration(), {}, statement::Block(), {}, std::move(m))); + return newBuilder(lastStatement()._trueNode()); + } + + auto addIf(Expression cond, Meta m = Meta()) { + _block._add(statement::If(std::move(cond), statement::Block(), {}, std::move(m))); + return newBuilder(lastStatement()._trueNode()); + } + + auto addIfElse(const statement::Declaration& init, Expression cond, Meta m = Meta()) { + _block._add( + statement::If(init.declaration(), std::move(cond), statement::Block(), statement::Block(), std::move(m))); + return std::make_pair(newBuilder(lastStatement()._trueNode()), + newBuilder(lastStatement()._falseNode())); + } + + auto addIfElse(const statement::Declaration& init, Meta m = Meta()) { + _block._add(statement::If(init.declaration(), {}, statement::Block(), statement::Block(), std::move(m))); + return std::make_pair(newBuilder(lastStatement()._trueNode()), + newBuilder(lastStatement()._falseNode())); + } + + auto addIfElse(Expression cond, Meta m = Meta()) { + _block._add(statement::If(std::move(cond), statement::Block(), statement::Block(), std::move(m))); + return std::make_pair(newBuilder(lastStatement()._trueNode()), + newBuilder(lastStatement()._falseNode())); + } + + auto addBlock(Meta m = Meta()) { + _block._add(statement::Block({}, std::move(m))); + return newBuilder(_block._lastStatementNode()); + } + + class SwitchProxy { + public: + SwitchProxy(Builder* b, statement::Switch& s) : _builder(b), _switch(s) {} // NOLINT + + auto addCase(Expression expr, Meta m = Meta()) { return _addCase({std::move(expr)}, std::move(m)); } + + auto addCase(std::vector exprs, Meta m = Meta()) { + return _addCase(std::move(exprs), std::move(m)); + } + + auto addDefault(Meta m = Meta()) { return _addCase({}, std::move(m)); } + + private: + std::shared_ptr _addCase(std::vector exprs, Meta m = Meta()) { + _switch._addCase(statement::switch_::Case(std::move(exprs), statement::Block(), std::move(m))); + return _builder->newBuilder(_switch._lastCaseNode().as()._bodyNode()); + } + + Builder* _builder; + statement::Switch& _switch; + }; + + auto addSwitch(Expression cond, Meta m = Meta()) { + _block._add(statement::Switch(std::move(cond), {}, std::move(m))); + return SwitchProxy(this, lastStatement()); + } + + auto addSwitch(const statement::Declaration& init, Expression cond, Meta m = Meta()) { + _block._add(statement::Switch(init.declaration(), std::move(cond), {}, std::move(m))); + return SwitchProxy(this, lastStatement()); + } + + class TryProxy { + public: + TryProxy(Builder* b, statement::Try& s) : _builder(b), _try(&s) {} //NOLINT(google-runtime-references) + + auto addCatch(declaration::Parameter p, Meta m = Meta()) { + _try->_addCatch(statement::try_::Catch(std::move(p), statement::Block(), std::move(m))); + return _builder->newBuilder(_try->_lastCatchNode().as()._bodyNode()); + } + + auto addCatch(Meta m = Meta()) { + _try->_addCatch(statement::try_::Catch(statement::Block(), std::move(m))); + return _builder->newBuilder(_try->_lastCatchNode().as()._bodyNode()); + } + + TryProxy(const TryProxy&) = default; + TryProxy(TryProxy&&) = default; + TryProxy() = delete; + ~TryProxy() = default; + TryProxy& operator=(const TryProxy&) = default; + TryProxy& operator=(TryProxy&&) noexcept = default; + + private: + Builder* _builder; + statement::Try* _try; + }; + + auto addTry(Meta m = Meta()) { + _block._add(statement::Try(statement::Block(), {}, std::move(m))); + return std::make_pair(newBuilder(lastStatement()._bodyNode()), + TryProxy(this, lastStatement())); + } + +private: + friend class SwitchProxy; + + Builder(std::shared_ptr context, Statement& s) // NOLINT + : _context(std::move(context)), _block(s.as()) {} + + template + T& lastStatement() { + return _block._lastStatementNode().as(); + } + + std::shared_ptr newBuilder(Node& n) { // NOLINT + return std::shared_ptr(new Builder(_context, n.template as())); + } + + std::shared_ptr _context; + std::optional _our_block; + statement::Block& _block; + + std::map _tmps; +}; + +} // namespace hilti::builder diff --git a/hilti/include/ast/builder/declaration.h b/hilti/include/ast/builder/declaration.h new file mode 100644 index 000000000..f61918f55 --- /dev/null +++ b/hilti/include/ast/builder/declaration.h @@ -0,0 +1,124 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace hilti::builder { + +inline auto import(std::string module, const Meta& m = Meta()) { + return declaration::ImportedModule(hilti::ID(std::move(module), m), std::string(".hlt"), m); +} + +inline auto import(std::string module, std::vector search_dirs, const Meta& m = Meta()) { + return declaration::ImportedModule(hilti::ID(std::move(module), m), std::string(".hlt"), {}, std::move(search_dirs), + m); +} + +inline auto import(std::string module, std::optional search_scope, std::vector search_dirs, + const Meta& m = Meta()) { + return declaration::ImportedModule(hilti::ID(std::move(module), m), std::string(".hlt"), std::move(search_scope), + std::move(search_dirs), m); +} + +inline auto local(ID id_, Type t, Meta m = Meta()) { + return statement::Declaration(declaration::LocalVariable(std::move(id_), std::move(t), {}, false, std::move(m))); +} + +inline auto local(ID id_, Expression init, Meta m = Meta()) { + return statement::Declaration(declaration::LocalVariable(std::move(id_), std::move(init), false, std::move(m))); +} + +inline auto local(ID id_, Type t, Expression init, Meta m = Meta()) { + return statement::Declaration( + declaration::LocalVariable(std::move(id_), std::move(t), std::move(init), false, std::move(m))); +} + +inline auto local(ID id_, Type t, std::vector args, Meta m = Meta()) { + return statement::Declaration( + declaration::LocalVariable(std::move(id_), std::move(t), std::move(args), {}, false, std::move(m))); +} + +inline auto global(ID id_, Type t, declaration::Linkage linkage = declaration::Linkage::Private, Meta m = Meta()) { + return declaration::GlobalVariable(std::move(id_), std::move(t), {}, linkage, std::move(m)); +} + +inline auto global(ID id_, Expression init, declaration::Linkage linkage = declaration::Linkage::Private, + Meta m = Meta()) { + return declaration::GlobalVariable(std::move(id_), std::move(init), linkage, std::move(m)); +} + +inline auto global(ID id_, Type t, Expression init, declaration::Linkage linkage = declaration::Linkage::Private, + Meta m = Meta()) { + return declaration::GlobalVariable(std::move(id_), std::move(t), std::move(init), linkage, std::move(m)); +} + +inline auto global(ID id_, Type t, std::vector args, + declaration::Linkage linkage = declaration::Linkage::Private, Meta m = Meta()) { + return declaration::GlobalVariable(std::move(id_), std::move(t), std::move(args), {}, linkage, std::move(m)); +} + +inline auto type(ID id, ::hilti::Type type, declaration::Linkage linkage = declaration::Linkage::Private, + Meta m = Meta()) { + return declaration::Type(std::move(id), std::move(type), linkage, std::move(m)); +} + +inline auto type(ID id, ::hilti::Type type, std::optional attrs, + declaration::Linkage linkage = declaration::Linkage::Private, Meta m = Meta()) { + return declaration::Type(std::move(id), std::move(type), std::move(attrs), linkage, std::move(m)); +} + +inline auto constant(ID id_, Expression init, declaration::Linkage linkage = declaration::Linkage::Private, + Meta m = Meta()) { + return declaration::Constant(std::move(id_), std::move(init), linkage, std::move(m)); +} + +inline auto parameter(ID id, Type type, type::function::parameter::Kind kind = type::function::parameter::Kind::In, + Meta m = Meta()) { + return type::function::Parameter(std::move(id), std::move(type), kind, {}, std::move(m)); +} + +inline auto parameter(ID id, Type type, Expression default_, + type::function::parameter::Kind kind = type::function::parameter::Kind::In, Meta m = Meta()) { + return type::function::Parameter(std::move(id), std::move(type), kind, std::move(default_), std::move(m)); +} + +template +static auto parameters(Params&&... params) { + return std::vector(std::forward(params)...); +} + +inline auto function(ID id, Type result, const std::vector& params, + type::function::Flavor flavor = type::function::Flavor::Standard, + declaration::Linkage linkage = declaration::Linkage::Private, + function::CallingConvention cc = function::CallingConvention::Standard, + std::optional attrs = {}, const Meta& m = Meta()) { + auto ft = type::Function(type::function::Result(std::move(result), m), params, flavor, m); + auto f = Function(std::move(id), std::move(ft), {}, cc, std::move(attrs), m); + return declaration::Function(std::move(f), linkage, m); +} + +inline auto function(ID id, Type result, const std::vector& params, Statement body, + type::function::Flavor flavor = type::function::Flavor::Standard, + declaration::Linkage linkage = declaration::Linkage::Private, + function::CallingConvention cc = function::CallingConvention::Standard, + std::optional attrs = {}, const Meta& m = Meta()) { + auto ft = type::Function(type::function::Result(std::move(result), m), params, flavor, m); + auto f = Function(std::move(id), std::move(ft), std::move(body), cc, std::move(attrs), m); + return declaration::Function(std::move(f), linkage, m); +} + +inline auto function(ID id, type::Function ftype, Statement body, + declaration::Linkage linkage = declaration::Linkage::Private, + function::CallingConvention cc = function::CallingConvention::Standard, + std::optional attrs = {}, const Meta& m = Meta()) { + auto f = Function(std::move(id), std::move(ftype), std::move(body), cc, std::move(attrs), m); + return declaration::Function(std::move(f), linkage, m); +} + +} // namespace hilti::builder diff --git a/hilti/include/ast/builder/expression.h b/hilti/include/ast/builder/expression.h new file mode 100644 index 000000000..4549df3d4 --- /dev/null +++ b/hilti/include/ast/builder/expression.h @@ -0,0 +1,314 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include + +namespace hilti::builder { + +// ID expression + +inline Expression id(ID id_, Meta m = Meta()) { return expression::UnresolvedID(std::move(id_), std::move(m)); } + +// Ctor expressions + +inline Expression string(std::string s, const Meta& m = Meta()) { + return expression::Ctor(ctor::String(std::move(s), m), m); +} + +inline Expression bool_(bool b, const Meta& m = Meta()) { return expression::Ctor(ctor::Bool(b, m), m); } + +inline Expression bytes(std::string s, const Meta& m = Meta()) { + return expression::Ctor(ctor::Bytes(std::move(s), m), m); +} + +inline Expression coerceTo(Expression e, Type t, const Meta& m) { + return expression::PendingCoerced(std::move(e), std::move(t), m); +} + +inline Expression coerceTo(const Expression& e, Type t) { + return expression::PendingCoerced(e, std::move(t), e.meta()); +} + +inline Expression default_(Type t, const Meta& m = Meta()) { + return expression::Ctor(ctor::Default(std::move(t), m), m); +} + +inline Expression default_(Type t, std::vector type_args, const Meta& m = Meta()) { + return expression::Ctor(ctor::Default(std::move(t), std::move(type_args), m), m); +} + +inline Expression exception(Type t, std::string msg, const Meta& m = Meta()) { + return expression::Ctor(ctor::Exception(std::move(t), builder::string(std::move(msg)), m), m); +} + +inline Expression exception(Type t, Expression msg, const Meta& m = Meta()) { + return expression::Ctor(ctor::Exception(std::move(t), std::move(msg), m), m); +} + +inline Expression integer(int i, const Meta& m = Meta()) { + return expression::Ctor(ctor::SignedInteger(static_cast(i), 64, m), m); +} + +inline Expression integer(int64_t i, const Meta& m = Meta()) { + return expression::Ctor(ctor::SignedInteger(i, 64, m), m); +} + +inline Expression integer(unsigned int i, const Meta& m = Meta()) { + return expression::Ctor(ctor::UnsignedInteger(i, 64, m), m); +} + +inline Expression integer(uint64_t i, const Meta& m = Meta()) { + return expression::Ctor(ctor::UnsignedInteger(i, 64, m), m); +} + +inline Expression null(const Meta& m = Meta()) { return expression::Ctor(ctor::Null(m), m); } + +inline Expression optional(Expression e, const Meta& m = Meta()) { + return expression::Ctor(ctor::Optional(std::move(e), m), m); +} + +inline Expression optional(Type t, const Meta& m = Meta()) { + return expression::Ctor(ctor::Optional(std::move(t), m), m); +} + +inline Expression port(hilti::ctor::Port::Value p, const Meta& m = Meta()) { + return expression::Ctor(ctor::Port(p, m), m); +} + +inline Expression regexp(std::string p, std::optional attrs = {}, const Meta& m = Meta()) { + return expression::Ctor(ctor::RegExp({std::move(p)}, std::move(attrs), m), m); +} + +inline Expression regexp(std::vector p, std::optional attrs = {}, const Meta& m = Meta()) { + return expression::Ctor(ctor::RegExp(std::move(p), std::move(attrs), m), m); +} + +inline Expression stream(std::string s, const Meta& m = Meta()) { + return expression::Ctor(ctor::Stream(std::move(s), m), m); +} + +inline Expression struct_(std::vector f, const Meta& m = Meta()) { + return expression::Ctor(ctor::Struct(std::move(f), m), m); +} + +inline Expression struct_(std::vector f, Type t, const Meta& m = Meta()) { + return expression::Ctor(ctor::Struct(std::move(f), std::move(t), m), m); +} + +inline Expression tuple(std::vector v, const Meta& m = Meta()) { + return expression::Ctor(ctor::Tuple(std::move(v), m), m); +} + +inline Expression vector(const std::vector& v, const Meta& m = Meta()) { + return expression::Ctor(ctor::Vector(v, m), m); +} + +inline Expression vector(Type t, std::vector v, const Meta& m = Meta()) { + return expression::Ctor(ctor::Vector(std::move(t), std::move(v), m), m); +} + +inline Expression vector(Type t, const Meta& m = Meta()) { + return expression::Ctor(ctor::Vector(std::move(t), {}, m), m); +} + +inline Expression void_(const Meta& m = Meta()) { return expression::Void(m); } + +inline Expression strong_reference(Type t, const Meta& m = Meta()) { + return expression::Ctor(ctor::StrongReference(std::move(t), m), m); +} + +inline Expression weak_reference(Type t, const Meta& m = Meta()) { + return expression::Ctor(ctor::WeakReference(std::move(t), m), m); +} + +inline Expression value_reference(Expression e, const Meta& m = Meta()) { + return expression::Ctor(ctor::ValueReference(std::move(e), m), m); +} + +// Operator expressions + +inline Expression and_(Expression op0, Expression op1, const Meta& m = Meta()) { + return expression::LogicalAnd(std::move(op0), std::move(op1), m); +} + +inline Expression or_(Expression op0, Expression op1, const Meta& m = Meta()) { + return expression::LogicalOr(std::move(op0), std::move(op1), m); +} + +inline Expression begin(Expression e, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Begin, {std::move(e)}, m); +} + +inline Expression cast(Expression e, Type dst, Meta m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Cast, {std::move(e), expression::Type_(std::move(dst))}, + std::move(m)); +} + +inline Expression deref(Expression e, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Deref, {std::move(e)}, m); +} + +inline Expression end(Expression e, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::End, {std::move(e)}, m); +} + +inline Expression call(ID id_, std::vector v, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Call, {id(std::move(id_), m), tuple(std::move(v), m)}, m); +} + +inline Expression index(Expression value, unsigned int index, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Index, {std::move(value), integer(index, m)}, m); +} + +inline Expression size(Expression op, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Size, {std::move(op)}, m); +} + +inline Expression modulo(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Modulo, {std::move(op1), std::move(op2)}, m); +} + +inline Expression lowerEqual(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::LowerEqual, {std::move(op1), std::move(op2)}, m); +} + +inline Expression greaterEqual(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::GreaterEqual, {std::move(op1), std::move(op2)}, m); +} + +inline Expression lower(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Lower, {std::move(op1), std::move(op2)}, m); +} + +inline Expression greater(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Greater, {std::move(op1), std::move(op2)}, m); +} + +inline Expression equal(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Equal, {std::move(op1), std::move(op2)}, m); +} + +inline Expression unequal(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Unequal, {std::move(op1), std::move(op2)}, m); +} + +inline Expression member(Expression self, std::string id_, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Member, + {std::move(self), expression::Member(ID(std::move(id_)), m)}, m); +} + +inline Expression memberCall(Expression self, std::string id_, std::vector v, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::MemberCall, + {std::move(self), expression::Member(ID(std::move(id_)), m), + tuple(std::move(v), m)}, + m); +} + +inline Expression unpack(Type type, std::vector args, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Unpack, + {hilti::expression::Type_(std::move(type), m), tuple(std::move(args), m)}, m); +} + +inline Expression sumAssign(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::SumAssign, {std::move(op1), std::move(op2)}, m); +} + +inline Expression deferred(Expression e, Meta m = Meta()) { return expression::Deferred(std::move(e), std::move(m)); } + +inline Expression differenceAssign(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::DifferenceAssign, {std::move(op1), std::move(op2)}, m); +} + +inline Expression sum(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Sum, {std::move(op1), std::move(op2)}, m); +} + +inline Expression difference(Expression op1, Expression op2, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::Difference, {std::move(op1), std::move(op2)}, m); +} + +inline Expression decrementPostfix(Expression op, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::DecrPostfix, {std::move(op)}, m); +} + +inline Expression decrementPrefix(Expression op, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::DecrPrefix, {std::move(op)}, m); +} + +inline Expression incrementPostfix(Expression op, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::IncrPostfix, {std::move(op)}, m); +} + +inline Expression incrementPrefix(Expression op, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::IncrPrefix, {std::move(op)}, m); +} + +inline Expression new_(Type t, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::New, + {expression::Type_(std::move(t), m), + hilti::expression::Ctor(hilti::ctor::Tuple({}, m))}, + m); +} + +inline Expression new_(Type t, std::vector args, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::New, + {expression::Type_(std::move(t), m), + hilti::expression::Ctor(hilti::ctor::Tuple(std::move(args), m))}, + m); +} + +// Other expressions + +inline Expression expression(Ctor c, Meta m = Meta()) { return expression::Ctor(std::move(c), std::move(m)); } + +inline Expression expression(const Location& l) { return expression::Ctor(ctor::String(l), l); } + +inline Expression expression(const Meta& m) { return expression::Ctor(ctor::String(m.location()), m); } + +inline Expression move(Expression e, Meta m = Meta()) { return expression::Move(std::move(e), std::move(m)); } + +inline Expression self(NodeRef d, Meta m = Meta()) { + return expression::Keyword(hilti::expression::keyword::Kind::Self, std::move(d), std::move(m)); +} + +inline Expression dollardollar(Type t, Meta m = Meta()) { + return expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, std::move(t), std::move(m)); +} + +inline Expression assign(Expression target, Expression src, Meta m = Meta()) { + return expression::Assign(std::move(target), std::move(src), std::move(m)); +} + +inline Expression not_(Expression e, Meta m = Meta()) { return expression::LogicalNot(std::move(e), std::move(m)); } + +inline Expression ternary(Expression cond, Expression true_, Expression false_, Meta m = Meta()) { + return expression::Ternary(std::move(cond), std::move(true_), std::move(false_), std::move(m)); +} + +inline Expression min(const Expression& e1, const Expression& e2, const Meta& m = Meta()) { + return ternary(lowerEqual(e1, e2, m), e1, e2, m); +} + +inline Expression max(const Expression& e1, const Expression& e2, const Meta& m = Meta()) { + return ternary(lowerEqual(e1, e2, m), e2, e1, m); +} + +inline Expression type_wrapped(Expression e, const Meta& m = Meta()) { + return expression::TypeWrapped(std::move(e), m); +} + +inline Expression type_wrapped(Expression e, Type t, const Meta& m = Meta()) { + return expression::TypeWrapped(std::move(e), std::move(t), m); +} + +inline Expression expect_type(Expression e, Type expected, const Meta& m = Meta()) { + return expression::TypeWrapped(e, std::move(expected), expression::TypeWrapped::ValidateTypeMatch(), + m ? std::move(m) : e.meta()); +} + +} // namespace hilti::builder diff --git a/hilti/include/ast/builder/type.h b/hilti/include/ast/builder/type.h new file mode 100644 index 000000000..a293a0006 --- /dev/null +++ b/hilti/include/ast/builder/type.h @@ -0,0 +1,11 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti::builder { + +inline Type typeByID(::hilti::ID id, Meta m = Meta()) { return hilti::type::UnresolvedID(std::move(id), std::move(m)); } + +} // namespace hilti::builder diff --git a/hilti/include/ast/ctor.api b/hilti/include/ast/ctor.api new file mode 100644 index 000000000..1da79f9c7 --- /dev/null +++ b/hilti/include/ast/ctor.api @@ -0,0 +1,49 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for HILTI constructors. */ +class Ctor(trait::isCtor) : trait::isNode { + /** Returns the HILTI `Type` of the constructor's value. */ + Type type() const; + + /** + * Returns true if the constructor's value is a value that will never + * change. + */ + bool isConstant() const; + + /** Returns true if the ctor can be the target of an assignment. */ + bool isLhs() const; + + /** + * Returns true if, when evaluated as RHS, the ctor will yield a + * temporary value. + */ + bool isTemporary() const; + + /** + * Returns true if the constructor's HILTI value is equivalent to another + * one. + */ + bool isEqual(const Ctor& other) const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + + /** Implements the `Node` interface. */ + const std::vector& childs() const; + + /** Implements the `Node` interface. */ + std::vector& childs(); + + /** Implements the `Node` interface. */ + const Meta& meta() const; + + /** Implements the `Node` interface. */ + void setMeta(Meta m); + + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/hilti/include/ast/ctor.h b/hilti/include/ast/ctor.h new file mode 100644 index 000000000..e7eeb4ad4 --- /dev/null +++ b/hilti/include/ast/ctor.h @@ -0,0 +1,48 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { + +namespace trait { +/** Trait for classes implementing the `Ctor` interface. */ +class isCtor : public isNode {}; +} // namespace trait + +namespace ctor { +namespace detail { +#include + +/** Creates an AST node representing a `Ctor`. */ +inline Node to_node(Ctor t) { return Node(std::move(t)); } + +/** Renders a constructor as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, Ctor c) { return out << to_node(std::move(c)); } + +inline bool operator==(const Ctor& x, const Ctor& y) { + if ( &x == &y ) + return true; + + assert(x.isEqual(y) == y.isEqual(x)); // Expected to be symmetric. + return x.isEqual(y); +} + +inline bool operator!=(const Ctor& c1, const Ctor& c2) { return ! (c1 == c2); } + +} // namespace detail +} // namespace ctor + +using Ctor = ctor::detail::Ctor; +using ctor::detail::to_node; + +/** Constructs an AST node from any class implementing the `Ctor` interface. */ +template::value>* = nullptr> +inline Node to_node(T t) { + return Node(Ctor(std::move(t))); +} + +} // namespace hilti diff --git a/hilti/include/ast/ctors/address.h b/hilti/include/ast/ctors/address.h new file mode 100644 index 000000000..beeb8424a --- /dev/null +++ b/hilti/include/ast/ctors/address.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a Address constructor. */ +class Address : public NodeBase, public hilti::trait::isCtor { +public: + using Value = hilti::rt::Address; + + Address(const Value& addr, Meta m = Meta()) : NodeBase(std::move(m)), _address(addr) {} + + const auto& value() const { return _address; } + + bool operator==(const Address& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Address(meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"address", to_string(_address)}}; } + +private: + Value _address; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/all.h b/hilti/include/ast/ctors/all.h new file mode 100644 index 000000000..c03b53ba7 --- /dev/null +++ b/hilti/include/ast/ctors/all.h @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/ctors/bool.h b/hilti/include/ast/ctors/bool.h new file mode 100644 index 000000000..d70e414e3 --- /dev/null +++ b/hilti/include/ast/ctors/bool.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a boolean constructor. */ +class Bool : public NodeBase, public hilti::trait::isCtor { +public: + Bool(bool v, Meta m = Meta()) : NodeBase(std::move(m)), _value(v) {} + + auto value() const { return _value; } + + bool operator==(const Bool& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Bool(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + bool _value; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/bytes.h b/hilti/include/ast/ctors/bytes.h new file mode 100644 index 000000000..a913e35c8 --- /dev/null +++ b/hilti/include/ast/ctors/bytes.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a bytes constructor. */ +class Bytes : public NodeBase, public hilti::trait::isCtor { +public: + Bytes(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + + auto value() const { return _value; } + + bool operator==(const Bytes& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Bytes(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return false; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + std::string _value; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/coerced.h b/hilti/include/ast/ctors/coerced.h new file mode 100644 index 000000000..bce743166 --- /dev/null +++ b/hilti/include/ast/ctors/coerced.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a constructor that's been coerced from one type to another. */ +class Coerced : public NodeBase, public hilti::trait::isCtor { +public: + Coerced(Ctor orig, Ctor new_, Meta m = Meta()) : NodeBase({std::move(orig), std::move(new_)}, std::move(m)) {} + + auto originalCtor() const { return child(0); } + auto coercedCtor() const { return child(1); } + + bool operator==(const Coerced& other) const { + return originalCtor() == other.originalCtor() && coercedCtor() == other.coercedCtor(); + } + + /** Implements `Ctor` interface. */ + Type type() const { return type::effectiveType(coercedCtor().type()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return coercedCtor().isConstant(); } + /** Implements `Ctor` interface. */ + auto isLhs() const { return coercedCtor().isLhs(); } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return coercedCtor().isTemporary(); } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/default.h b/hilti/include/ast/ctors/default.h new file mode 100644 index 000000000..29e19532f --- /dev/null +++ b/hilti/include/ast/ctors/default.h @@ -0,0 +1,61 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a constructor for a type's default value. */ +class Default : public NodeBase, public hilti::trait::isCtor { +public: + /** Constructs a default value of type `t`. */ + Default(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + + /** + * Constructs a default value of type `t`, passing specified arguments to + * types with parameters. + */ + Default(Type t, std::vector type_args, Meta m = Meta()) + : NodeBase(nodes(std::move(t), std::move(type_args)), std::move(m)) {} + + auto typeArguments() const { return childs(1, -1); } + + bool operator==(const Default& other) const { return type() == other.type(); } + + /** Implements `Ctor` interface. */ + Type type() const { return type::effectiveType(child(0)); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + bool isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new local default constructor with the type argument expressions replaced. + * + * @param d original declaration + * @param i new init expresssion + * @return new declaration that's equal to original one but with the init expression replaced + */ + static Ctor setTypeArguments(const Default& d, std::vector args) { + auto x = Ctor(d)._clone().as(); + x.childs() = x.childs(0, 1); + for ( auto&& a : args ) + x.childs().emplace_back(std::move(a)); + + return std::move(x); + } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/enum.h b/hilti/include/ast/ctors/enum.h new file mode 100644 index 000000000..7a13ce835 --- /dev/null +++ b/hilti/include/ast/ctors/enum.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { + +namespace ctor { + +/** AST node for an enum constructor. */ +class Enum : public NodeBase, public hilti::trait::isCtor { +public: + Enum(type::enum_::Label v, type::Enum t, Meta m = Meta()) : NodeBase({std::move(v), std::move(t)}, std::move(m)) {} + Enum(type::enum_::Label v, NodeRef td, Meta m = Meta()) + : NodeBase({std::move(v), node::none}, std::move(m)), _type_decl(std::move(td)) {} + + auto value() const { return childs()[0].as(); } + + bool operator==(const Enum& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { + return type::effectiveType(_type_decl ? type::effectiveType((*_type_decl)->as().type()) : + childs()[1].as()); + } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + std::optional _type_decl; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/error.h b/hilti/include/ast/ctors/error.h new file mode 100644 index 000000000..b72a6ea61 --- /dev/null +++ b/hilti/include/ast/ctors/error.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for an error constructor. */ +class Error : public NodeBase, public hilti::trait::isCtor { +public: + Error(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + + auto value() const { return _value; } + + bool operator==(const Error& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Error(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + std::string _value; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/exception.h b/hilti/include/ast/ctors/exception.h new file mode 100644 index 000000000..c83e0a3db --- /dev/null +++ b/hilti/include/ast/ctors/exception.h @@ -0,0 +1,37 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a string constructor. */ +class Exception : public NodeBase, public hilti::trait::isCtor { +public: + Exception(Type t, Expression msg, Meta m = Meta()) : NodeBase({std::move(t), std::move(msg)}, std::move(m)) {} + + auto value() const { return child(1); } + + bool operator==(const Exception& other) const { return type() == other.type() && value() == other.value(); } + + /** Implements `Ctor` interface. */ + Type type() const { return type::effectiveType(child(0)); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/integer.h b/hilti/include/ast/ctors/integer.h new file mode 100644 index 000000000..dde244cc7 --- /dev/null +++ b/hilti/include/ast/ctors/integer.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti { +namespace ctor { + +namespace detail { + +// CHECK: IntegerBase = isCtor +/** Base class for AST nodes for both signed and unsigned integer constructors. */ +template +class IntegerBase : public NodeBase, public hilti::trait::isCtor { +public: + IntegerBase(T v, int w, const Meta& m = Meta()) : NodeBase(m), _value(v), _width(w) {} + + auto value() const { return _value; } + auto width() const { return _width; } + + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto type() const { return S(_width); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}, {"width", _width}}; } + +private: + T _value; + int _width; +}; + +} // namespace detail + +/** AST node for a signed integer constructor. */ +class SignedInteger : public detail::IntegerBase { +public: + using Base = detail::IntegerBase; + using Base::IntegerBase; + + SignedInteger(uint64_t v, int w, const Meta& m = Meta()) : Base::IntegerBase(static_cast(v), w, m) {} + + bool operator==(const SignedInteger& other) const { return value() == other.value() && width() == other.width(); } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } +}; + +/** AST node for a unsigned integer constructor. */ +class UnsignedInteger : public detail::IntegerBase { +public: + using detail::IntegerBase::IntegerBase; + + bool operator==(const UnsignedInteger& other) const { return value() == other.value() && width() == other.width(); } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/interval.h b/hilti/include/ast/ctors/interval.h new file mode 100644 index 000000000..7734c2ea2 --- /dev/null +++ b/hilti/include/ast/ctors/interval.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a interval constructor. */ +class Interval : public NodeBase, public hilti::trait::isCtor { +public: + using Value = hilti::rt::Interval; + + Interval(const Value& interval, Meta m = Meta()) : NodeBase(std::move(m)), _interval(interval) {} + + const auto& value() const { return _interval; } + + bool operator==(const Interval& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Interval(meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"interval", to_string(_interval)}}; } + +private: + Value _interval; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/list.h b/hilti/include/ast/ctors/list.h new file mode 100644 index 000000000..a6f8d21ca --- /dev/null +++ b/hilti/include/ast/ctors/list.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace hilti { +namespace ctor { + +/** AST node for a List constructor. */ +class List : public NodeBase, public hilti::trait::isCtor { +public: + List(std::vector e, Meta m = Meta()) : NodeBase(nodes(inferType(e, m), e), m) {} + List(Type t, std::vector e, Meta m = Meta()) + : NodeBase(nodes(std::move(t), std::move(e)), std::move(m)) {} + + auto elementType() const { return type::effectiveType(child(0)); } + auto value() const { return childs(1, -1); } + + bool operator==(const List& other) const { + return elementType() == other.elementType() && value() == other.value(); + } + + /** Implements `Ctor` interface. */ + auto type() const { return type::List(elementType(), meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return false; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + Type inferType(const std::vector& e, const Meta& /* m */) { + return e.size() ? e.front().type() : type::unknown; + } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/map.h b/hilti/include/ast/ctors/map.h new file mode 100644 index 000000000..fb2d4ea2f --- /dev/null +++ b/hilti/include/ast/ctors/map.h @@ -0,0 +1,71 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace hilti { +namespace ctor { + +/** AST node for a map constructor. */ +class Map : public NodeBase, public hilti::trait::isCtor { +public: + using Element = std::pair; + Map(const std::vector& e, const Meta& m = Meta()) : NodeBase(nodes(_inferTypes(e, m), _flatten(e)), m) {} + Map(Type key, Type value, const std::vector& e, Meta m = Meta()) + : NodeBase(nodes(std::move(key), std::move(value), _flatten(e)), std::move(m)) {} + + auto keyType() const { return type::effectiveType(child(0)); } + auto elementType() const { return type::effectiveType(child(1)); } + + auto value() const { + auto exprs = childs(2, -1); + std::vector elems; + for ( auto&& i = exprs.begin(); i != exprs.end(); i += 2 ) + elems.emplace_back(std::make_pair(std::move(*i), std::move(*(i + 1)))); + return elems; + } + + bool operator==(const Map& other) const { + return keyType() == other.keyType() && elementType() == other.elementType() && value() == other.value(); + } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Map(keyType(), elementType(), meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return false; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + std::vector _inferTypes(const std::vector& e, const Meta& /* m */) { + if ( e.size() ) + return {e.front().first.type(), e.front().second.type()}; + + return {type::unknown, type::unknown}; + } + + std::vector _flatten(const std::vector& elems) { + std::vector exprs; + for ( auto&& e : elems ) { + exprs.emplace_back(e.first); + exprs.emplace_back(e.second); + } + + return exprs; + } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/network.h b/hilti/include/ast/ctors/network.h new file mode 100644 index 000000000..5352acaa4 --- /dev/null +++ b/hilti/include/ast/ctors/network.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a Network constructor. */ +class Network : public NodeBase, public hilti::trait::isCtor { +public: + using Value = hilti::rt::Network; + + Network(const Value& network, Meta m = Meta()) : NodeBase(std::move(m)), _network(network) {} + + const auto& value() const { return _network; } + + bool operator==(const Network& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Network(meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"network", to_string(_network)}}; } + +private: + Value _network; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/null.h b/hilti/include/ast/ctors/null.h new file mode 100644 index 000000000..00ec5e44c --- /dev/null +++ b/hilti/include/ast/ctors/null.h @@ -0,0 +1,34 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a null constructor. */ +class Null : public NodeBase, public hilti::trait::isCtor { +public: + Null(Meta m = Meta()) : NodeBase(std::move(m)) {} + + bool operator==(const Null& /* other */) const { return true; } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Null(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/optional.h b/hilti/include/ast/ctors/optional.h new file mode 100644 index 000000000..d1b341fe9 --- /dev/null +++ b/hilti/include/ast/ctors/optional.h @@ -0,0 +1,57 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a constructor for an optional value. */ +class Optional : public NodeBase, public hilti::trait::isCtor { +public: + /** Constructs a set value. */ + Optional(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + + /** Constructs an unset value of type `t`. */ + Optional(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + + std::optional value() const { return childs()[0].tryAs(); } + + Type dereferencedType() const { + if ( auto x = childs()[0].tryAs() ) + return x->type(); + + return type::effectiveType(child(0)); + } + + bool operator==(const Optional& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + Type type() const { return type::Optional(dereferencedType(), meta()); } + + /** Implements `Ctor` interface. */ + bool isConstant() const { + if ( auto e = value() ) + return e->isConstant(); + + return true; + } + + /** Implements `Ctor` interface. */ + bool isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/port.h b/hilti/include/ast/ctors/port.h new file mode 100644 index 000000000..e4a14b001 --- /dev/null +++ b/hilti/include/ast/ctors/port.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a port constructor. */ +class Port : public NodeBase, public hilti::trait::isCtor { +public: + using Value = hilti::rt::Port; + + Port(const Value& port, Meta m = Meta()) : NodeBase(std::move(m)), _port(port) {} + + const auto& value() const { return _port; } + + bool operator==(const Port& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Port(meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"port", to_string(_port)}}; } + +private: + Value _port; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/real.h b/hilti/include/ast/ctors/real.h new file mode 100644 index 000000000..d51a29cbb --- /dev/null +++ b/hilti/include/ast/ctors/real.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a double precision floating-point constructor. */ +class Real : public NodeBase, public hilti::trait::isCtor { +public: + Real(double v, Meta m = Meta()) : NodeBase(std::move(m)), _value(v) {} + + auto value() const { return _value; } + + bool operator==(const Real& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Real(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + double _value; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/reference.h b/hilti/include/ast/ctors/reference.h new file mode 100644 index 000000000..9c7d54b42 --- /dev/null +++ b/hilti/include/ast/ctors/reference.h @@ -0,0 +1,95 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a constructor for a `ref` value (which can only be null). */ +class StrongReference : public NodeBase, public hilti::trait::isCtor { +public: + /** Constructs a null value of type `t`. */ + StrongReference(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + + Type dereferencedType() const { return type::effectiveType(child(0)); } + + bool operator==(const StrongReference& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements `Ctor` interface. */ + Type type() const { return type::StrongReference(dereferencedType(), meta()); } + + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + bool isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +/** AST node for a constructor for a `weak_ref` value (which can only be null). */ +class WeakReference : public NodeBase, public hilti::trait::isCtor { +public: + /** Constructs a null value of type `t`. */ + WeakReference(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + + Type dereferencedType() const { return type::effectiveType(child(0)); } + + bool operator==(const WeakReference& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements `Ctor` interface. */ + Type type() const { return type::WeakReference(dereferencedType(), meta()); } + + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + bool isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +/** AST node for a constructor for a `value_ref` instance. */ +class ValueReference : public NodeBase, public hilti::trait::isCtor { +public: + /** Constructs a reference value of type `t`. */ + ValueReference(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + + const Expression& expression() const { return child(0); } + Type dereferencedType() const { return child(0).type(); } + + bool operator==(const ValueReference& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements `Ctor` interface. */ + Type type() const { return type::ValueReference(dereferencedType(), meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + bool isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/regexp.h b/hilti/include/ast/ctors/regexp.h new file mode 100644 index 000000000..d6c7ce5b5 --- /dev/null +++ b/hilti/include/ast/ctors/regexp.h @@ -0,0 +1,44 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a RegExp constructor. */ +class RegExp : public NodeBase, public hilti::trait::isCtor { +public: + RegExp(std::vector p, std::optional attrs = {}, Meta m = Meta()) + : NodeBase(nodes(std::move(attrs)), std::move(m)), _patterns(std::move(p)) {} + + auto attributes() const { return childs()[0].tryAs(); } + const auto& value() const { return _patterns; } + + auto isNoSub() const { return AttributeSet::find(attributes(), "&nosub"); } + + bool operator==(const RegExp& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::RegExp(meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"patterns", util::join(_patterns, " | ")}}; } + +private: + std::vector _patterns; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/result.h b/hilti/include/ast/ctors/result.h new file mode 100644 index 000000000..c3f965ea0 --- /dev/null +++ b/hilti/include/ast/ctors/result.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a constructor for a result value. */ +class Result : public NodeBase, public hilti::trait::isCtor { +public: + Result(Expression v, Meta m = Meta()) : NodeBase({std::move(v)}, std::move(m)) {} + + std::optional value() const { + auto e = child(0); + + if ( e.type() != type::Error() ) + return std::move(e); + + return {}; + } + + std::optional error() const { + auto e = child(0); + + if ( e.type() == type::Error() ) + return std::move(e); + + return {}; + } + + std::optional dereferencedType() const { + if ( auto x = value() ) + return x->type(); + + return {}; + } + + bool operator==(const Result& other) const { return value() == other.value() && error() == other.error(); } + + /** Implements `Ctor` interface. */ + Type type() const { + if ( auto v = value() ) + return type::Result(v->type(), meta()); + + return type::Result(type::Any(), meta()); + } + + /** Implements `Ctor` interface. */ + bool isConstant() const { + if ( auto v = value() ) + return v->isConstant(); + + return true; + } + + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/set.h b/hilti/include/ast/ctors/set.h new file mode 100644 index 000000000..a4028f9c1 --- /dev/null +++ b/hilti/include/ast/ctors/set.h @@ -0,0 +1,47 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace hilti { +namespace ctor { + +/** AST node for a set constructor. */ +class Set : public NodeBase, public hilti::trait::isCtor { +public: + Set(const std::vector& e, const Meta& m = Meta()) : NodeBase(nodes(_inferType(e, m), e), m) {} + Set(Type t, std::vector e, Meta m = Meta()) + : NodeBase(nodes(std::move(t), std::move(e)), std::move(m)) {} + + auto elementType() const { return type::effectiveType(child(0)); } + auto value() const { return childs(1, -1); } + + bool operator==(const Set& other) const { return elementType() == other.elementType() && value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Set(elementType(), meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return false; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + Type _inferType(const std::vector& e, const Meta& /* m */) { + return e.size() ? e.front().type() : type::unknown; + } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/stream.h b/hilti/include/ast/ctors/stream.h new file mode 100644 index 000000000..3b50d1bbc --- /dev/null +++ b/hilti/include/ast/ctors/stream.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a stream constructor. */ +class Stream : public NodeBase, public hilti::trait::isCtor { +public: + Stream(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + + auto value() const { return _value; } + + bool operator==(const Stream& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Stream(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return false; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + std::string _value; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/string.h b/hilti/include/ast/ctors/string.h new file mode 100644 index 000000000..1549eb98c --- /dev/null +++ b/hilti/include/ast/ctors/string.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a string constructor. */ +class String : public NodeBase, public hilti::trait::isCtor { +public: + String(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + + auto value() const { return _value; } + + bool operator==(const String& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::String(); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + std::string _value; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/struct.h b/hilti/include/ast/ctors/struct.h new file mode 100644 index 000000000..ed4f3e30b --- /dev/null +++ b/hilti/include/ast/ctors/struct.h @@ -0,0 +1,72 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace ctor { + +namespace struct_ { +/** A struct field initialization in the form of key/value. */ +using Field = std::pair; +} // namespace struct_ + +/** AST node for a struct constructor. */ +class Struct : public NodeBase, public hilti::trait::isCtor { +public: + Struct(std::vector f, Meta m = Meta()) + : NodeBase(nodes(type::unknown, std::move(f)), std::move(m)) {} + Struct(std::vector f, Type t, Meta m = Meta()) + : NodeBase(nodes(std::move(t), std::move(f)), std::move(m)) {} + + /** Returns all field IDs that the constructors initialized. */ + auto ids() const { return childsOfType(); } + + /** Returns all field values that the constructors initializes. */ + auto values() const { return childsOfType(); } + + /** Returns all fields that the constructors initialized. */ + auto fields() const { return util::zip2(ids(), values()); } + + /** Returns a field initialized by the constructor by its ID. */ + std::optional field(const ID& id) const { + for ( auto f : fields() ) { + if ( f.first == id ) + return f; + } + + return {}; + } + + bool operator==(const Struct& other) const { return ids() == other.ids() && values() == other.values(); } + + /** Implements `Ctor` interface. */ + Type type() const { + if ( auto t = childs()[0].as(); ! t.isA() ) + return type::effectiveType(t); + + auto f = util::transform(fields(), [](auto& x) { + return type::struct_::Field(x.first, x.second.type(), {}, x.first.meta()); + }); + return type::Struct(f, meta()); + } + + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/time.h b/hilti/include/ast/ctors/time.h new file mode 100644 index 000000000..cb0d43c71 --- /dev/null +++ b/hilti/include/ast/ctors/time.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a time constructor. */ +class Time : public NodeBase, public hilti::trait::isCtor { +public: + using Value = hilti::rt::Time; + + Time(const Value& time, Meta m = Meta()) : NodeBase(std::move(m)), _time(time) {} + + const auto& value() const { return _time; } + + bool operator==(const Time& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Time(meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"time", to_string(_time)}}; } + +private: + Value _time; +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/tuple.h b/hilti/include/ast/ctors/tuple.h new file mode 100644 index 000000000..b77cdb3cd --- /dev/null +++ b/hilti/include/ast/ctors/tuple.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a tuple constructor. */ +class Tuple : public NodeBase, public hilti::trait::isCtor { +public: + Tuple(std::vector v, Meta m = Meta()) : NodeBase(nodes(std::move(v)), std::move(m)) {} + + auto value() const { return childs(0, -1); } + + bool operator==(const Tuple& other) const { return value() == other.value(); } + + /** Implements `Ctor` interface. */ + auto type() const { + auto v1 = value(); + auto v2 = std::vector{}; + std::transform(v1.begin(), v1.end(), std::back_inserter(v2), [](const Expression& e) { return e.type(); }); + return type::Tuple(v2, meta()); + } + + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + + /** Implements `Ctor` interface. */ + auto isLhs() const { + if ( value().empty() ) + return false; + + for ( const auto& e : value() ) { + if ( ! e.isLhs() ) + return false; + } + + return true; + } + + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/union.h b/hilti/include/ast/ctors/union.h new file mode 100644 index 000000000..c3b082cdf --- /dev/null +++ b/hilti/include/ast/ctors/union.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** AST node for a struct constructor. */ +class Union : public NodeBase, public hilti::trait::isCtor { +public: + Union(Type unit_type, Expression value, Meta m = Meta()) + : NodeBase(nodes(std::move(unit_type), std::move(value)), std::move(m)) {} + + /** Returns the value to initialize the unit with. */ + Expression value() const { return child(1); } + + bool operator==(const Union& other) const { return type() == other.type() && value() == other.value(); } + + /** Implements `Ctor` interface. */ + Type type() const { return child(0); } + + /** Implements `Ctor` interface. */ + bool isConstant() const { return true; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/ctors/vector.h b/hilti/include/ast/ctors/vector.h new file mode 100644 index 000000000..f6385e370 --- /dev/null +++ b/hilti/include/ast/ctors/vector.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace hilti { +namespace ctor { + +/** AST node for a vector constructor. */ +class Vector : public NodeBase, public hilti::trait::isCtor { +public: + Vector(const std::vector& e, const Meta& m = Meta()) : NodeBase(nodes(_inferType(e, m), e), m) {} + Vector(Type t, std::vector e, Meta m = Meta()) + : NodeBase(nodes(std::move(t), std::move(e)), std::move(m)) {} + + auto elementType() const { return type::effectiveType(child(0)); } + auto value() const { return childs(1, -1); } + + bool operator==(const Vector& other) const { + return elementType() == other.elementType() && value() == other.value(); + } + + /** Implements `Ctor` interface. */ + auto type() const { return type::Vector(elementType(), meta()); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return false; } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + Type _inferType(const std::vector& e, const Meta& /* m */) { + return e.size() ? e.front().type() : type::unknown; + } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/include/ast/declaration.api b/hilti/include/ast/declaration.api new file mode 100644 index 000000000..4b48f115b --- /dev/null +++ b/hilti/include/ast/declaration.api @@ -0,0 +1,45 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for HILTI declarations. */ +class Declaration(trait::isDeclaration) : trait::isNode { + /** Returns the declaration's ID. */ + ID id() const; + + /** Returns the declaration's linkage. */ + Linkage linkage() const; + + /** + * Returns true if one cannot assign to what this declaration defines. + */ + bool isConstant() const; + + /** + * Returns a user-friendly name for the type of object the declararion + * refers to. This is used in error messages. + */ + std::string displayName() const; + + /** Returns true if the declaration is equivalent to another one in HILTI semantics. */ + bool isEqual(const Declaration& other) const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + + /** Implements the `Node` interface. */ + const std::vector& childs() const; + + /** Implements the `Node` interface. */ + std::vector& childs(); + + /** Implements the `Node` interface. */ + const Meta& meta() const; + + /** Implements the `Node` interface. */ + void setMeta(Meta m); + + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/hilti/include/ast/declaration.h b/hilti/include/ast/declaration.h new file mode 100644 index 000000000..db6e8a6d7 --- /dev/null +++ b/hilti/include/ast/declaration.h @@ -0,0 +1,79 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { + +namespace trait { +/** Trait for classes implementing the `Declaration` interface. */ +class isDeclaration : public isNode {}; +} // namespace trait + +namespace declaration { + +/** Linkage defining visibility/accessability of a declaration. */ +enum class Linkage { + Init, /// executes automatically at startup, not otherwise accessible + Struct, /// method inside a method + Private, /// accessible only locally + Public, /// accessible across modules +}; + +namespace detail { +constexpr util::enum_::Value linkages[] = { + {Linkage::Struct, "method"}, + {Linkage::Public, "public"}, + {Linkage::Private, "private"}, + {Linkage::Init, "init"}, +}; +} // namespace detail + +/** Returns the HILTI string representation corresponding to a linkage. */ +constexpr auto to_string(Linkage f) { return util::enum_::to_string(f, detail::linkages); } + +namespace linkage { +/** + * Parses a HILTI string representation of a linkage. + * + * @exception `std::out_of_range` if the string does not map to a linkage + */ +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, detail::linkages); } +} // namespace linkage + +namespace detail { + +#include + +/** Creates an AST node representing a `Declaration`. */ +inline Node to_node(Declaration t) { return Node(std::move(t)); } + +/** Renders a declaration as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, Declaration d) { return out << to_node(std::move(d)); } + +inline bool operator==(const Declaration& x, const Declaration& y) { + if ( &x == &y ) + return true; + + assert(x.isEqual(y) == y.isEqual(x)); // Expected to be symmetric. + return x.isEqual(y); +} + +inline bool operator!=(const Declaration& d1, const Declaration& d2) { return ! (d1 == d2); } + +} // namespace detail +} // namespace declaration + +using Declaration = declaration::detail::Declaration; +using declaration::detail::to_node; + +/** Constructs an AST node from any class implementing the `Declaration` interface. */ +template::value>* = nullptr> +inline Node to_node(T t) { + return Node(Declaration(std::move(t))); +} + +} // namespace hilti diff --git a/hilti/include/ast/declarations/all.h b/hilti/include/ast/declarations/all.h new file mode 100644 index 000000000..d5f2bce39 --- /dev/null +++ b/hilti/include/ast/declarations/all.h @@ -0,0 +1,14 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/declarations/constant.h b/hilti/include/ast/declarations/constant.h new file mode 100644 index 000000000..7dcb50fb6 --- /dev/null +++ b/hilti/include/ast/declarations/constant.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of a constant. */ +class Constant : public NodeBase, public hilti::trait::isDeclaration { +public: + Constant(ID id, hilti::Expression value, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase({std::move(id), std::move(value)}, std::move(m)), _linkage(linkage) {} + + auto value() const { return child(1); } + + bool operator==(const Constant& other) const { return id() == other.id() && value() == other.value(); } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return _linkage; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "constant"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } + +private: + Linkage _linkage; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/expression.h b/hilti/include/ast/declarations/expression.h new file mode 100644 index 000000000..2153c3b15 --- /dev/null +++ b/hilti/include/ast/declarations/expression.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of an expression. */ +class Expression : public NodeBase, public hilti::trait::isDeclaration { +public: + Expression(ID id, hilti::Expression e, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(e), node::none), std::move(m)), _linkage(linkage) {} + Expression(ID id, hilti::Expression e, std::optional attrs, Linkage linkage = Linkage::Private, + Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(e), std::move(attrs)), std::move(m)), _linkage(linkage) {} + + auto expression() const { return child(1); } + auto attributes() const { return childs()[2].tryAs(); } + + bool operator==(const Expression& other) const { return id() == other.id() && expression() == other.expression(); } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return _linkage; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "expression"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } + +private: + Linkage _linkage; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/forward.h b/hilti/include/ast/declarations/forward.h new file mode 100644 index 000000000..741f9bc65 --- /dev/null +++ b/hilti/include/ast/declarations/forward.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +#include + +namespace hilti { +namespace declaration { + +/** + * AST node for a declaration that forwards all methods to another one. This + * is useful to bind to nodes with declarations that may later be replaced. + * Note that this is not meant to be used as the original definition of a + * declaration itself; the code generator won't emit any corresponding + * declaration for it. + */ +class Forward : public NodeBase, public hilti::trait::isDeclaration { +public: + using Callback = std::function; + + Forward(Callback cb, Meta m = Meta()) : NodeBase(std::move(m)), _cb(std::move(cb)) {} + + auto callback() const { return _cb; } + + bool operator==(const Forward& other) const { return _cb() == other._cb(); } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return _cb().isConstant(); } + /** Implements `Declaration` interface. */ + ID id() const { return _cb().id(); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return _cb().linkage(); } + /** Implements `Declaration` interface. */ + std::string displayName() const { return _cb().displayName() + " (forwarded)"; } + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{}}; } + +private: + Callback _cb; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/function.h b/hilti/include/ast/declarations/function.h new file mode 100644 index 000000000..8c4faf5a9 --- /dev/null +++ b/hilti/include/ast/declarations/function.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of an function. */ +class Function : public NodeBase, public hilti::trait::isDeclaration { +public: + Function(::hilti::Function function, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase({std::move(function)}, std::move(m)), _linkage(linkage) {} + + auto function() const { return child<::hilti::Function>(0); } + + bool operator==(const Function& other) const { return id() == other.id() && function() == other.function(); } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + ID id() const { return function().id(); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return _linkage; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "function"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } + + /** + * Returns a new function declaration with the body replaced. + * + * @param d original declaration + * @param b new body + * @return new declaration that's equal to original one but with the body replaced + */ + static Declaration setBody(const Function& d, const Statement& b) { + auto x = Declaration(d)._clone().as(); + x.childs()[0] = hilti::Function::setBody(x.childs()[0].as<::hilti::Function>(), b); + return x; + } + +private: + Linkage _linkage; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/global-variable.h b/hilti/include/ast/declarations/global-variable.h new file mode 100644 index 000000000..d63dc8a9c --- /dev/null +++ b/hilti/include/ast/declarations/global-variable.h @@ -0,0 +1,120 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of global variable. */ +class GlobalVariable : public NodeBase, public hilti::trait::isDeclaration { +public: + GlobalVariable(ID id, ::hilti::Type type, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), node::none), std::move(m)), _linkage(linkage) {} + + GlobalVariable(ID id, ::hilti::Type type, std::optional init = {}, + Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(init)), std::move(m)), _linkage(linkage) {} + + GlobalVariable(ID id, ::hilti::Type type, std::vector args, + std::optional init = {}, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(init), std::move(args)), std::move(m)), + _linkage(linkage) {} + + GlobalVariable(ID id, hilti::Expression init, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase(nodes(std::move(id), node::none, std::move(init)), std::move(m)), _linkage(linkage) {} + + auto init() const { return childs()[2].tryAs(); } + auto typeArguments() const { return childs(3, -1); } + + ::hilti::Type type() const { + if ( auto t = childs()[1].tryAs<::hilti::Type>(); t && *t != type::unknown ) + return type::effectiveType(std::move(*t)); + + if ( auto i = init() ) + return i->type(); + + return type::unknown; + } + + /** + * Returns true if this is an `auto` variable, i.e., the type is derived + * from the initialization expression. + */ + auto hasAutomaticType() const { return ! childs()[1].isA<::hilti::Type>(); } + + bool operator==(const GlobalVariable& other) const { + return id() == other.id() && type() == other.type() && init() == other.init(); + } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return false; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return _linkage; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "global variable"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } + + /** + * Returns a new global variable declaration with its type replaced. + * + * @param d original declaration + * @param b new type + * @return new declaration that's equal to original one but with the type replaced + */ + static Declaration setType(const GlobalVariable& d, std::optional t) { + auto x = Declaration(d)._clone().as(); + if ( t ) + x.childs()[1] = *t; + else + x.childs()[1] = node::none; + + return std::move(x); + } + + /** + * Returns a new global variable declaration with the init expression replaced. + * + * @param d original declaration + * @param b new init expresssion + * @return new declaration that's equal to original one but with the init expression replaced + */ + static Declaration setInit(const GlobalVariable& d, const hilti::Expression& i) { + auto x = Declaration(d)._clone().as(); + x.childs()[2] = i; + return std::move(x); + } + + /** + * Returns a new global variable declaration with the type argument expressions replaced. + * + * @param d original declaration + * @param i new init expresssion + * @return new declaration that's equal to original one but with the init expression replaced + */ + static Declaration setTypeArguments(const GlobalVariable& d, std::vector args) { + auto x = Declaration(d)._clone().as(); + x.childs() = x.childs(0, 3); + for ( auto&& a : args ) + x.childs().emplace_back(std::move(a)); + + return std::move(x); + } + +private: + Linkage _linkage; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/imported-module.h b/hilti/include/ast/declarations/imported-module.h new file mode 100644 index 000000000..94cd10dbc --- /dev/null +++ b/hilti/include/ast/declarations/imported-module.h @@ -0,0 +1,89 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of imported module. */ +class ImportedModule : public NodeBase, public hilti::trait::isDeclaration { +public: + ImportedModule(ID id, const std::string& search_extension, Meta m = Meta()) + : NodeBase({std::move(id)}, std::move(m)), _extension(search_extension) {} + + ImportedModule(ID id, const std::string& search_extension, std::optional search_scope, Meta m = Meta()) + : NodeBase({std::move(id)}, std::move(m)), _extension(search_extension), _scope(std::move(search_scope)) {} + + ImportedModule(ID id, const std::string& search_extension, std::optional search_scope, + std::vector search_dirs, Meta m = Meta()) + : NodeBase({std::move(id)}, std::move(m)), + _extension(search_extension), + _scope(std::move(search_scope)), + _dirs(std::move(search_dirs)) {} + + ImportedModule(ID id, std::filesystem::path path, Meta m = Meta()) + : NodeBase({std::move(id)}, std::move(m)), _path(std::move(path)) {} + + Result module() const { + if ( _module ) + return _module->template as(); + + return result::Error("module reference not initialized yet"); + } + + auto extension() const { return _extension; } + auto path() const { return _path; } + auto scope() const { return _scope; } + const auto& searchDirectories() const { return _dirs; } + + bool operator==(const ImportedModule& other) const { return id() == other.id(); } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return Linkage::Private; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "imported module"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { + return node::Properties{{"extension", _extension.native()}, + {"path", _path.native()}, + {"scope", (_scope ? _scope->str() : std::string("-"))}}; + } + + /** + * Returns a new imported module declaration with the module reference replaced. + * + * @param d original declaration + * @param n new module reference + * @return new declaration that's equal to original one but with the module reference replaced + */ + static Declaration setModule(const ImportedModule& d, NodeRef n) { + auto x = Declaration(d)._clone().as(); + x._module = std::move(n); + return x; + } + +private: + NodeRef _module; + std::filesystem::path _extension; + std::filesystem::path _path; + std::optional _scope; + std::vector _dirs; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/local-variable.h b/hilti/include/ast/declarations/local-variable.h new file mode 100644 index 000000000..cca37b537 --- /dev/null +++ b/hilti/include/ast/declarations/local-variable.h @@ -0,0 +1,122 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of a local variable. */ +class LocalVariable : public NodeBase, public hilti::trait::isDeclaration { +public: + LocalVariable(ID id, ::hilti::Type type, std::optional init = {}, bool const_ = false, + Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(init)), std::move(m)), _const(const_) {} + + LocalVariable(ID id, ::hilti::Type type, std::vector args, + std::optional init = {}, bool const_ = false, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(init), std::move(args)), std::move(m)), + _const(const_) {} + + LocalVariable(ID id, hilti::Expression init, bool const_ = false, Meta m = Meta()) + : NodeBase(nodes(std::move(id), node::none, std::move(init)), std::move(m)), _const(const_) {} + + auto init() const { return childs()[2].tryAs(); } + auto typeArguments() const { return childs(3, -1); } + + ::hilti::Type type() const { + if ( auto t = childs()[1].tryAs<::hilti::Type>(); t && *t != type::unknown ) + return type::effectiveType(std::move(*t)); + + if ( auto i = init() ) + return i->type(); + + return type::unknown; + } + + /** + * Returns true if this is an `auto` variable, i.e., the type is derived + * from the initialization expression. + */ + auto hasAutomaticType() const { return ! childs()[1].isA<::hilti::Type>(); } + + bool operator==(const LocalVariable& other) const { + return id() == other.id() && type() == other.type() && init() == other.init(); + } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return _const; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return Linkage::Private; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "local variable"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"const", _const}}; } + + /** + * Returns a new local variable declaration with the type replaced. + * + * @param d original declaration + * @param b new type + * @return new declaration that's equal to original one but with the type replaced + */ + static Declaration setType(const LocalVariable& d, std::optional t) { + auto x = Declaration(d)._clone().as(); + if ( t ) + x.childs()[1] = *t; + else + x.childs()[1] = node::none; + + return std::move(x); + } + + /** + * Returns a new local variable declaration with the init expression replaced. + * + * @param d original declaration + * @param i new init expresssion + * @return new declaration that's equal to original one but with the init expression replaced + */ + static Declaration setInit(const LocalVariable& d, std::optional i) { + auto x = Declaration(d)._clone().as(); + if ( i ) + x.childs()[2] = *i; + else + x.childs()[2] = node::none; + + return std::move(x); + } + + /** + * Returns a new local variable declaration with the type argument expressions replaced. + * + * @param d original declaration + * @param i new init expresssion + * @return new declaration that's equal to original one but with the init expression replaced + */ + static Declaration setTypeArguments(const LocalVariable& d, std::vector args) { + auto x = Declaration(d)._clone().as(); + // We keep the first 3 children, which are ID, type and init expression. + x.childs() = x.childs(0, 3); + for ( auto&& a : args ) + x.childs().emplace_back(std::move(a)); + + return std::move(x); + } + +private: + bool _const; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/parameter.h b/hilti/include/ast/declarations/parameter.h new file mode 100644 index 000000000..5459aff66 --- /dev/null +++ b/hilti/include/ast/declarations/parameter.h @@ -0,0 +1,132 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace declaration { + +namespace parameter { + +/** Type of a `Parameter`. */ +enum class Kind { + Unknown, /**< not specified */ + Copy, /**< `copy` parameter */ + In, /**< `in` parameter */ + InOut /**< `inout` parameter */ +}; + +namespace detail { +constexpr util::enum_::Value kinds[] = { + {Kind::Unknown, "unknown"}, + {Kind::Copy, "copy"}, + {Kind::In, "in"}, + {Kind::InOut, "inout"}, +}; +} // namespace detail + +constexpr auto to_string(Kind k) { return util::enum_::to_string(k, detail::kinds); } + +namespace kind { +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, detail::kinds); } +} // namespace kind + +} // namespace parameter + +/** AST node for a declaration of a function parameter. */ +class Parameter : public NodeBase, public hilti::trait::isDeclaration { +public: + Parameter(ID id, hilti::Type type, parameter::Kind kind, std::optional default_, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(default_)), std::move(m)), _kind(kind) {} + + Parameter(ID id, hilti::Type type, parameter::Kind kind, std::optional default_, + bool is_struct_param, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(default_)), std::move(m)), + _kind(kind), + _is_struct_param(is_struct_param) {} + + Parameter() : NodeBase({node::none, node::none, node::none}, Meta()) {} + + auto type() const { return type::effectiveType(child(1)); } + + auto default_() const { return childs()[2].tryAs(); } + auto kind() const { return _kind; } + auto isStructParameter() const { return _is_struct_param; } + + bool operator==(const Parameter& other) const { + return id() == other.id() && type() == other.type() && kind() == other.kind() && default_() == other.default_(); + } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return _kind == parameter::Kind::In; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return Linkage::Private; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "parameter"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { + return node::Properties{{"kind", to_string(_kind)}, {"is_struct_param", _is_struct_param}}; + } + + /** + * Returns a new parameter declaration with its type replaced. + * + * @param d original declaration + * @param b new type + * @return new declaration that's equal to original one but with the type replaced + */ + static Declaration setType(const Parameter& d, std::optional t) { + auto x = Declaration(d)._clone().as(); + if ( t ) + x.childs()[1] = *t; + else + x.childs()[1] = node::none; + + return x; + } + + /** + * Returns a new parameter declaration with the default expression replaced. + * + * @param d original declaration + * @param e new default expresssion + * @return new declaration that's equal to original one but with the default expression replaced + */ + static Declaration setDefault(const Parameter& d, const hilti::Expression& e) { + auto x = Declaration(d)._clone().as(); + x.childs()[2] = e; + return x; + } + + /** + * Returns a new parameter declaration with the is-struct-parameter option set. + * @param d original declaration + * @return new declaration that's equal to original one but with the flag set + */ + static Declaration setIsStructParameter(const Parameter& d) { + auto x = Declaration(d)._clone().as(); + x._is_struct_param = true; + return x; + } + +private: + parameter::Kind _kind = parameter::Kind::Unknown; + bool _is_struct_param = false; +}; + +/** Returns true if two parameters are different only by name of their ID. */ +inline bool areEquivalent(const Parameter& p1, const Parameter& p2) { + return p1.type() == p2.type() && p1.kind() == p2.kind() && p1.default_() == p2.default_(); +} + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/property.h b/hilti/include/ast/declarations/property.h new file mode 100644 index 000000000..6dfaa9ba6 --- /dev/null +++ b/hilti/include/ast/declarations/property.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a declaration of a module property. */ +class Property : public NodeBase, public hilti::trait::isDeclaration { +public: + Property(ID id, Meta m = Meta()) : NodeBase(nodes(std::move(id), node::none), std::move(m)) {} + + Property(ID id, hilti::Expression attr, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(attr)), std::move(m)) {} + + auto expression() const { return childs()[1].tryAs(); } + + bool operator==(const Property& other) const { return id() == other.id() && expression() == other.expression(); } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return Linkage::Private; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "property"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/declarations/type.h b/hilti/include/ast/declarations/type.h new file mode 100644 index 000000000..4553acbd1 --- /dev/null +++ b/hilti/include/ast/declarations/type.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a type declaration. */ +class Type : public NodeBase, public hilti::trait::isDeclaration { +public: + Type(ID id, ::hilti::Type type, Linkage linkage = Linkage::Private, Meta m = Meta()) + : NodeBase({std::move(id), std::move(type), node::none}, std::move(m)), _linkage(linkage) {} + + Type(ID id, ::hilti::Type type, std::optional attrs, Linkage linkage = Linkage::Private, + Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(attrs)), std::move(m)), _linkage(linkage) {} + + auto type() const { return type::effectiveType(child(1)); } + auto attributes() const { return childs()[2].tryAs(); } + + bool isOnHeap() const { + if ( type::isOnHeap(type()) ) + return true; + + auto x = attributes(); + return x && x->find("&on-heap"); + } + + /** Shortcut to `type::typeID()` for the declared type. */ + auto typeID() const { return childs()[1].as().typeID(); } + + /** Shortcut to `type::cxxID()` for the declared type. */ + auto cxxID() const { return childs()[1].as().cxxID(); } + + bool operator==(const Type& other) const { return id() == other.id() && type() == other.type(); } + + /** Internal method for use by builder API only. */ + auto& _typeNode() { return childs()[1]; } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + const ID& id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return _linkage; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "type"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } + + /** + * Returns a new type declaration with the resolving type replaced. + * + * @param d original declaration + * @param t new type + * @return new declaration that's equal to original one but with the resolving type replaced + */ + static Type setType(const Type& d, const ::hilti::Type& t) { + auto x = Declaration(d)._clone().as(); + x.childs()[1] = t; + return x; + } + +private: + Linkage _linkage; +}; + +} // namespace declaration +} // namespace hilti diff --git a/hilti/include/ast/detail/operator-registry.h b/hilti/include/ast/detail/operator-registry.h new file mode 100644 index 000000000..23fc2a55b --- /dev/null +++ b/hilti/include/ast/detail/operator-registry.h @@ -0,0 +1,67 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +namespace hilti { +namespace operator_ { + +/** Singleton registering available operators. */ +class Registry { +public: + using OperatorMap = std::map>; + + /** Returns a map of all available operators. */ + const OperatorMap& all() const { return _operators; } + + /** Returns a map of all available operators. */ + const std::vector& allOfKind(Kind kind) const { + if ( auto x = _operators.find(kind); x != _operators.end() ) + return x->second; + + logger().internalError("unregistered operator requested in allOfKind()"); + } + + /** Registers an Operator as available. */ + void register_(Kind kind, Operator info) { _operators[kind].push_back(std::move(info)); } + + void printDebug() { +#if 0 + // Can't print this at registratin time as that's happening through + // global constructors. + for ( auto a : _operators ) { + for ( const auto& info : a.second ) { + int status; + auto n = abi::__cxa_demangle(info.typename_().c_str(), nullptr, nullptr, &status); + HILTI_DEBUG(logging::debug::Overloads, util::fmt("registered %s for operator '%s'", (n ? n : info.typename_().c_str()), to_string(info.kind()))); + } + } +#endif + } + + /** Returns a singleton instance of the current class. */ + static auto& singleton() { + static Registry instance; + return instance; + } + +private: + OperatorMap _operators; +}; + +/** Helper class to register an operator on instantiation. */ +class Register { +public: + Register(Kind k, Operator c) { Registry::singleton().register_(k, std::move(c)); } +}; + +inline auto registry() { return Registry::singleton(); } + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/detail/visitor.h b/hilti/include/ast/detail/visitor.h new file mode 100644 index 000000000..b0a85b74a --- /dev/null +++ b/hilti/include/ast/detail/visitor.h @@ -0,0 +1,8 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include diff --git a/hilti/include/ast/expression.api b/hilti/include/ast/expression.api new file mode 100644 index 000000000..12e239ee8 --- /dev/null +++ b/hilti/include/ast/expression.api @@ -0,0 +1,46 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for HILTI expressions. */ +class Expression(hilti::trait::isExpression) : hilti::trait::isNode { + /** Returns the expressions's HILTI type when evaluated. */ + hilti::Type type() const; + + /** + * Returns true if the constructor's value is a value that will never + * change. + */ + bool isConstant() const; + + /** Returns true if the expression is equivalent to another one in HILTI semantics. */ + bool isEqual(const Expression& other) const; + + /** Returns true if the expression can be the target of an assignment. */ + bool isLhs() const; + + /** + * Returns true if, when evaluated as RHS, the expression will yield a + * temporary value. + */ + bool isTemporary() const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + + /** Implements the `Node` interface. */ + const std::vector& childs() const; + + /** Implements the `Node` interface. */ + std::vector& childs(); + + /** Implements the `Node` interface. */ + const Meta& meta() const; + + /** Implements the `Node` interface. */ + void setMeta(Meta m); + + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/hilti/include/ast/expression.h b/hilti/include/ast/expression.h new file mode 100644 index 000000000..51a9ef5d7 --- /dev/null +++ b/hilti/include/ast/expression.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include + +namespace hilti { + +namespace trait { +/** Trait for classes implementing the `Expression` interface. */ +class isExpression : public isNode {}; +} // namespace trait + +namespace expression { +namespace detail { +#include + +/** Creates an AST node representing a `Expression`. */ +inline Node to_node(Expression t) { return Node(std::move(t)); } + +/** Renders an expression as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, Expression e) { return out << to_node(std::move(e)); } + +inline bool operator==(const Expression& x, const Expression& y) { + if ( &x == &y ) + return true; + + assert(x.isEqual(y) == y.isEqual(x)); // Expected to be symmetric. + return x.isEqual(y); +} + +inline bool operator!=(const Expression& e1, const Expression& e2) { return ! (e1 == e2); } + +} // namespace detail + +} // namespace expression + +using Expression = expression::detail::Expression; +using expression::detail::to_node; + +/** Constructs an AST node from any class implementing the `Expression` interface. */ +template::value>* = nullptr> +inline Node to_node(T t) { + return Node(Expression(std::move(t))); +} + +} // namespace hilti diff --git a/hilti/include/ast/expressions/all.h b/hilti/include/ast/expressions/all.h new file mode 100644 index 000000000..175c59501 --- /dev/null +++ b/hilti/include/ast/expressions/all.h @@ -0,0 +1,23 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/expressions/assign.h b/hilti/include/ast/expressions/assign.h new file mode 100644 index 000000000..19bd9628b --- /dev/null +++ b/hilti/include/ast/expressions/assign.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace expression { + +/** AST node for an assign expression. */ +class Assign : public NodeBase, public trait::isExpression { +public: + Assign(Expression target, Expression src, Meta m = Meta()) + : NodeBase({std::move(target), std::move(src)}, std::move(m)) {} + + auto target() const { return child(0); } + auto source() const { return child(1); } + + bool operator==(const Assign& other) const { return target() == other.target() && source() == other.source(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return target().isLhs(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return isLhs(); } + /** Implements `Expression` interface. */ + auto type() const { return type::effectiveType(target().type()); } + /** Implements `Expression` interface. */ + auto isConstant() const { return false; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new assign expression with the target expression replaced. + * + * @param d original expression + * @param t new target expresssion + * @return new expression that's equal to original one but with the target expression replaced + */ + static Expression setTarget(const Assign& e, const Expression& t) { + auto x = Expression(e)._clone().as(); + x.childs()[0] = t; + return x; + } + + /** + * Returns a new assign expression with the source expression replaced. + * + * @param d original expression + * @param t new source expresssion + * @return new expression that's equal to original one but with the source expression replaced + */ + static Expression setSource(const Assign& e, const Expression& s) { + auto x = Expression(e)._clone().as(); + x.childs()[1] = s; + return x; + } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/coerced.h b/hilti/include/ast/expressions/coerced.h new file mode 100644 index 000000000..5700a19fc --- /dev/null +++ b/hilti/include/ast/expressions/coerced.h @@ -0,0 +1,35 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace expression { + +/** AST node for an expression that's being coerced from one type to another. */ +class Coerced : public NodeBase, public trait::isExpression { +public: + Coerced(Expression e, Type t, Meta m = Meta()) : NodeBase({std::move(e), std::move(t)}, std::move(m)) {} + + auto expression() const { return child(0); } + + bool operator==(const Coerced& other) const { return expression() == other.expression() && type() == other.type(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return expression().isLhs(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + Type type() const { return type::nonConstant(type::effectiveType(child(1))); } + /** Implements `Expression` interface. */ + auto isConstant() const { return expression().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/ctor.h b/hilti/include/ast/expressions/ctor.h new file mode 100644 index 000000000..0e0e2fddd --- /dev/null +++ b/hilti/include/ast/expressions/ctor.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a constructor expression. */ +class Ctor : public NodeBase, public trait::isExpression { +public: + Ctor(hilti::Ctor c, Meta m = Meta()) : NodeBase({std::move(c)}, std::move(m)) {} + + auto ctor() const { return child<::hilti::Ctor>(0); } + + bool operator==(const Ctor& other) const { return ctor() == other.ctor(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return ctor().isLhs(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return ctor().isTemporary(); } + /** Implements `Expression` interface. */ + auto type() const { return type::effectiveType(ctor().type()); } + /** Implements `Expression` interface. */ + auto isConstant() const { return ctor().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/deferred.h b/hilti/include/ast/expressions/deferred.h new file mode 100644 index 000000000..30c194ac2 --- /dev/null +++ b/hilti/include/ast/expressions/deferred.h @@ -0,0 +1,48 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace expression { + +/** + * AST node for an expression for which evaluation is deferred at runtime to + * a later point when explicity requested by the runtime system. Optionally, + * that later evaluation can catch any exceptions and return a corresponding + * ``result``. + */ +class Deferred : public NodeBase, public trait::isExpression { +public: + Deferred(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + Deferred(Expression e, bool catch_exception, Meta m = Meta()) + : NodeBase({std::move(e)}, std::move(m)), _catch_exception(catch_exception) {} + + auto expression() const { return child(0); } + bool catchException() const { return _catch_exception; } + + bool operator==(const Deferred& other) const { return expression() == other.expression(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { + return _catch_exception ? Type(type::Result(expression().type(), meta())) : expression().type(); + } + /** Implements `Expression` interface. */ + auto isConstant() const { return expression().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"catch_exception", _catch_exception}}; } + +private: + bool _catch_exception; +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/id.h b/hilti/include/ast/expressions/id.h new file mode 100644 index 000000000..aaac96725 --- /dev/null +++ b/hilti/include/ast/expressions/id.h @@ -0,0 +1,77 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a expression representing a resolved ID. */ +class ResolvedID : public NodeBase, hilti::trait::isExpression { +public: + ResolvedID(ID id, NodeRef r, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)), _node(std::move(r)) { + assert(_node && _node->isA()); + } + + auto id() const { return child(0); } + auto declaration() const { + assert(_node); + return _node->as(); + } + bool isValid() const { return static_cast(_node); } + + bool operator==(const ResolvedID& other) const { + return id() == other.id() && declaration() == other.declaration(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { return ! declaration().isConstant(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return false; } + /** Implements `Expression` interface. */ + Type type() const; + /** Implements `Expression` interface. */ + bool isConstant() const; + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { + return _node ? node::Properties{{"resolved", _node.renderedRid()}} : node::Properties{{}}; + } + +private: + NodeRef _node; +}; + +/** AST node for a expression representing an unresolved ID. */ +class UnresolvedID : public NodeBase, hilti::trait::isExpression { +public: + UnresolvedID(ID id, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)) {} + + auto id() const { return child(0); } + + bool operator==(const UnresolvedID& other) const { return id() == other.id(); } + + // Expression interface. + bool isLhs() const { return true; } + bool isTemporary() const { return false; } + Type type() const { return type::unknown; } + auto isConstant() const { return false; } + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/keyword.h b/hilti/include/ast/expressions/keyword.h new file mode 100644 index 000000000..4453dfe8d --- /dev/null +++ b/hilti/include/ast/expressions/keyword.h @@ -0,0 +1,86 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace expression { + +namespace keyword { +// Type of a reserved keyword +enum class Kind { + Self, /**< `self` */ + DollarDollar /**< `$$` */ +}; + +namespace detail { +constexpr util::enum_::Value kinds[] = {{Kind::Self, "self"}, {Kind::DollarDollar, "$$"}}; +} // namespace detail + +namespace kind { +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, detail::kinds); } +} // namespace kind + +constexpr auto to_string(Kind m) { return util::enum_::to_string(m, detail::kinds); } + +} // namespace keyword + +/** AST node for an expression representing a reservered keyword. */ +class Keyword : public NodeBase, public hilti::trait::isExpression { +public: + Keyword(keyword::Kind kind, Meta m = Meta()) : NodeBase({type::unknown}, std::move(m)), _kind(kind) {} + Keyword(keyword::Kind kind, Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)), _kind(kind) {} + Keyword(keyword::Kind kind, NodeRef d, Meta m = Meta()) + : NodeBase({node::none}, std::move(m)), _kind(kind), _decl(d) {} + + keyword::Kind kind() const { return _kind; } + + bool operator==(const Keyword& other) const { return _kind == other._kind && type() == other.type(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return true; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return false; } + /** Implements `Expression` interface. */ + Type type() const { + auto t = type::effectiveType(_decl ? (**_decl).as().type() : childs()[0].as()); + + if ( _kind == keyword::Kind::Self ) + t = type::removeFlags(t, type::Flag::Constant); + + return t; + } + + /** Implements `Expression` interface. */ + auto isConstant() const { return false; } + + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"kind", to_string(_kind)}}; } + + /** + * Returns a new keyword expression with the resulting type replaced. + * + * @param d original expression + * @param t new type + * @return new expression that's equal to original one but with the resulting type replaced + */ + static Expression setType(const Keyword& e, const Type& t) { + auto x = Expression(e)._clone().as(); + x.childs()[0] = t; + x._decl = {}; + return x; + } + +private: + keyword::Kind _kind; + std::optional _decl; +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/list-comprehension.h b/hilti/include/ast/expressions/list-comprehension.h new file mode 100644 index 000000000..f58d75d28 --- /dev/null +++ b/hilti/include/ast/expressions/list-comprehension.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a vector comprehension expression. */ +class ListComprehension : public NodeBase, public trait::isExpression { +public: + ListComprehension(Expression input, Expression output, ID id, std::optional cond, Meta m = Meta()) + : NodeBase(nodes(std::move(input), std::move(output), std::move(id), std::move(cond)), std::move(m)) { + _computeType(); + } + + auto input() const { return child(0); } + auto output() const { return child(1); } + auto id() const { return child(2); } + auto condition() const { return childs()[3].tryAs(); } + + /** + * Returns the output expressions's scope. Note that the scope is shared + * among any copies of an instance. + */ + std::shared_ptr scope() const { return childs()[1].scope(); } + + bool operator==(const ListComprehension& other) const { + return input() == other.input() && output() == other.output() && id() == other.id() && + condition() == other.condition(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return child(4); } + + /** Implements `Expression` interface. */ + auto isConstant() const { return input().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + void _computeType() { + auto t = type::Computed(NodeRef(childs()[1]), [](auto& n) -> Type { + if ( auto x = n.template as().type(); ! x.template isA() ) + return type::List(std::move(x), n.meta()); + else + return type::unknown; + }); + + addChild(Type(std::move(t))); + } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/logical-and.h b/hilti/include/ast/expressions/logical-and.h new file mode 100644 index 000000000..69d8c5918 --- /dev/null +++ b/hilti/include/ast/expressions/logical-and.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a logical "and" expression. */ +class LogicalAnd : public NodeBase, public trait::isExpression { +public: + LogicalAnd(Expression op0, Expression op1, Meta m = Meta()) + : NodeBase({std::move(op0), std::move(op1)}, std::move(m)) {} + + auto op0() const { return child(0); } + auto op1() const { return child(1); } + + bool operator==(const LogicalAnd& other) const { return op0() == other.op0() && op1() == other.op1(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return type::Bool(); } + /** Implements `Expression` interface. */ + auto isConstant() const { return op0().isConstant() && op1().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new "and" expression with the first operand expression replaced. + * + * @param e original expression + * @param op new operand expresssion + * @return new expression that's equal to original one but with the operand replaced + */ + static Expression setOp0(const LogicalAnd& e, const Expression& op) { + auto x = Expression(e)._clone().as(); + x.childs()[0] = op; + return x; + } + + /** + * Returns a new "and" expression with the second operand expression replaced. + * + * @param e original expression + * @param op new operand expresssion + * @return new expression that's equal to original one but with the operand replaced + */ + static Expression setOp1(const LogicalAnd& e, const Expression& op) { + auto x = Expression(e)._clone().as(); + x.childs()[1] = op; + return x; + } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/logical-not.h b/hilti/include/ast/expressions/logical-not.h new file mode 100644 index 000000000..6a31a5971 --- /dev/null +++ b/hilti/include/ast/expressions/logical-not.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a logical "not" expression. */ +class LogicalNot : public NodeBase, public trait::isExpression { +public: + LogicalNot(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + + auto expression() const { return child(0); } + + bool operator==(const LogicalNot& other) const { return expression() == other.expression(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return type::Bool(); } + /** Implements `Expression` interface. */ + auto isConstant() const { return expression().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new "not" expression with the operand expression replaced. + * + * @param e original expression + * @param op new operand expresssion + * @return new expression that's equal to original one but with the operand replaced + */ + static Expression setExpression(const LogicalNot& e, const Expression& op) { + auto x = Expression(e)._clone().as(); + x.childs()[0] = op; + return x; + } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/logical-or.h b/hilti/include/ast/expressions/logical-or.h new file mode 100644 index 000000000..85958417c --- /dev/null +++ b/hilti/include/ast/expressions/logical-or.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a logical "or" expression. */ +class LogicalOr : public NodeBase, public trait::isExpression { +public: + LogicalOr(Expression op0, Expression op1, Meta m = Meta()) + : NodeBase({std::move(op0), std::move(op1)}, std::move(m)) {} + + auto op0() const { return child(0); } + auto op1() const { return child(1); } + + bool operator==(const LogicalOr& other) const { return op0() == other.op0() && op1() == other.op1(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return type::Bool(); } + /** Implements `Expression` interface. */ + auto isConstant() const { return op0().isConstant() && op1().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new "or" expression with the first operand expression replaced. + * + * @param e original expression + * @param op new operand expresssion + * @return new expression that's equal to original one but with the operand replaced + */ + static Expression setOp0(const LogicalOr& e, const Expression& op) { + auto x = Expression(e)._clone().as(); + x.childs()[0] = op; + return x; + } + + /** + * Returns a new "or" expression with the second operand expression replaced. + * + * @param e original expression + * @param op new operand expresssion + * @return new expression that's equal to original one but with the operand replaced + */ + static Expression setOp1(const LogicalOr& e, const Expression& op) { + auto x = Expression(e)._clone().as(); + x.childs()[1] = op; + return x; + } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/member.h b/hilti/include/ast/expressions/member.h new file mode 100644 index 000000000..f5dc0b434 --- /dev/null +++ b/hilti/include/ast/expressions/member.h @@ -0,0 +1,44 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a member-access expression. */ +class Member : public NodeBase, hilti::trait::isExpression { +public: + Member(ID id, Meta m = Meta()) : NodeBase({id, Type(type::Member(std::move(id))), node::none}, std::move(m)) {} + Member(ID id, Type member_type, Meta m = Meta()) + : NodeBase({id, std::move(member_type), Type(type::Member(std::move(id)))}, std::move(m)) {} + + auto id() const { return child(0); } + auto memberType() const { return type::effectiveOptionalType(childs()[1].tryAs()); } + + bool operator==(const Member& other) const { return id() == other.id() && type() == other.type(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return true; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return false; } + /** Implements `Expression` interface. */ + Type type() const { return type::effectiveType(child(1)); } + /** Implements `Expression` interface. */ + auto isConstant() const { return true; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/move.h b/hilti/include/ast/expressions/move.h new file mode 100644 index 000000000..d8a6074e3 --- /dev/null +++ b/hilti/include/ast/expressions/move.h @@ -0,0 +1,35 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace expression { + +/** AST node for a "move" expression. */ +class Move : public NodeBase, public trait::isExpression { +public: + Move(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + + auto expression() const { return child(0); } + + bool operator==(const Move& other) const { return expression() == other.expression(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return expression().type(); } + /** Implements `Expression` interface. */ + auto isConstant() const { return expression().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/pending-coerced.h b/hilti/include/ast/expressions/pending-coerced.h new file mode 100644 index 000000000..4efbe79db --- /dev/null +++ b/hilti/include/ast/expressions/pending-coerced.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace expression { + +/** AST node for an expression that will be coerced from one type to another. + * The actual coercion expression will be generated later and replace the + * this node during the apply-coercions phase. + */ +class PendingCoerced : public NodeBase, public trait::isExpression { +public: + PendingCoerced(Expression e, Type t, Meta m = Meta()) : NodeBase({std::move(e), std::move(t)}, std::move(m)) {} + + auto expression() const { return child(0); } + + bool operator==(const PendingCoerced& other) const { + return expression() == other.expression() && type() == other.type(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { return expression().isLhs(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return expression().isTemporary(); } + /** Implements `Expression` interface. */ + Type type() const { return type::effectiveType(child(1)); } + /** Implements `Expression` interface. */ + auto isConstant() const { return expression().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/resolved-operator.api b/hilti/include/ast/expressions/resolved-operator.api new file mode 100644 index 000000000..f16742ca0 --- /dev/null +++ b/hilti/include/ast/expressions/resolved-operator.api @@ -0,0 +1,75 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for expressions representing resolved operators. */ +class ResolvedOperator(trait::isResolvedOperator) : trait::isExpression { + /** Returns the operator expression that the expression has been resolved to. */ + Operator operator_() const; + + /** Returns the type of the operator expressions's result. */ + hilti::Type result() const; + + /** Returns the operator expression's operands. */ + std::vector operands() const; + + /** + * Returns the operator expression's 1st operand. + * @exception `std::out_of_range`` if the expression doesn't have that operand + */ + Expression op0() const; + + /** + * Returns the operator expression's 2nd operand. + * @exception `std::out_of_range`` if the expression doesn't have that operand + */ + Expression op1() const; + + /** + * Returns the operator expression's 3rd operand + * @exception `std::out_of_range`` if the expression doesn't have that operand + */ + Expression op2() const; + + /** Returns true if the operator expression has at least one operand. */ + bool hasOp0(); + + /** Returns true if the operator expression has at least two operands. */ + bool hasOp1(); + + /** Returns true if the operator expression has at least three operands. */ + bool hasOp2(); + + /** Sets the operator expressions 1st operand. */ + void setOp0(Expression e); + + /** Sets the operator expressions 2nd operand. */ + void setOp1(Expression e); + + /** Sets the operator expressions 3rd operand. */ + void setOp2(Expression e); + + /** Implements `Expression` interface. */ + bool isLhs() const; + /** Implements `Expression` interface. */ + bool isTemporary() const; + /** Implements the `Expression` interface. */ + hilti::Type type() const; + /** Implements the `Expression` interface. */ + bool isEqual(const Expression& other) const; + /** Implements the `Expression` interface. */ + bool isConstant() const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + /** Implements the `Node` interface. */ + const std::vector& childs() const; + /** Implements the `Node` interface. */ + std::vector& childs(); + /** Implements the `Node` interface. */ + const Meta& meta() const; + /** Implements the `Node` interface. */ + void setMeta(Meta m); + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/hilti/include/ast/expressions/resolved-operator.h b/hilti/include/ast/expressions/resolved-operator.h new file mode 100644 index 000000000..41066a642 --- /dev/null +++ b/hilti/include/ast/expressions/resolved-operator.h @@ -0,0 +1,131 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { + +namespace trait { +class isResolvedOperator {}; +} // namespace trait + +namespace expression { +namespace resolved_operator { +namespace detail { +#include + +inline Node to_node(ResolvedOperator t) { return Node(std::move(t)); } + +inline std::ostream& operator<<(std::ostream& out, ResolvedOperator i) { return out << to_node(std::move(i)); } + +} // namespace detail +} // namespace resolved_operator + +using ResolvedOperator = resolved_operator::detail::ResolvedOperator; +using resolved_operator::detail::to_node; + +/** + * Base class for an AST node for an expression representing a resolved operator usage. + * + * @note Typically, one derives from this only by using the `__BEGIN_OPERATOR` macro. + */ +class ResolvedOperatorBase : public NodeBase, public trait::isExpression, public trait::isResolvedOperator { +public: + ResolvedOperatorBase(const Operator& op, const std::vector& operands, Meta meta = Meta()) + : NodeBase(nodes(op.result(operands), operands), std::move(meta)), _operator(op) {} + + auto& operator_() const { return _operator; } + auto kind() const { return _operator.kind(); } + + // ResolvedOperator interface with common implementation. + auto operands() const { return childs(1, -1); } + auto result() const { + if ( ! childs()[0].isA() ) + return child(0); + else + // If the result couldn't be computed yet at instantiation time, + // try again. + return _operator.result(operands()); + } + + auto op0() const { return child(1); } + auto op1() const { return child(2); } + auto op2() const { return child(3); } + auto hasOp0() const { return ! childs().empty(); } + auto hasOp1() const { return childs().size() >= 3; } + auto hasOp2() const { return childs().size() >= 4; } + void setOp0(const Expression& e) { childs()[1] = e; } + void setOp1(const Expression& e) { childs()[2] = e; } + void setOp2(const Expression& e) { childs()[3] = e; } + + bool operator==(const ResolvedOperator& other) const { + return operator_() == other.operator_() && operands() == other.operands(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { return operator_().isLhs(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return isLhs(); } + /** Implements `Expression` interface. */ + auto type() const { return type::effectiveType(result()); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Expression` interface. */ + bool isConstant() const { return type::isConstant(type()); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"kind", to_string(_operator.kind())}}; } + +private: + ::hilti::operator_::detail::Operator _operator; +}; + +namespace resolved_operator { + +/** + * Copies an existing resolved operator, replacing its 1st operand with a different expression. + * + * @param r original operator + * @param e new operand + * @return new resolved operator with the 1st operand replaced + */ +inline hilti::Expression setOp0(const expression::ResolvedOperator& r, Expression e) { + auto x = r._clone().as(); + x.setOp0(std::move(e)); + return x; +} + +/** + * Copies an existing resolved operator, replacing its 2nd operand with a different expression. + * + * @param r original operator + * @param e new operand + * @return new resolved operator with the 2nd operand replaced + */ +inline hilti::Expression setOp1(const expression::ResolvedOperator& r, Expression e) { + auto x = r._clone().as(); + x.setOp1(std::move(e)); + return x; +} + +/** + * Copies an existing resolved operator, replacing its 3rd operand with a different expression. + * + * @param r original operator + * @param e new operand + * @return new resolved operator with the 3rd operand replaced + */ +inline hilti::Expression setOp2(const expression::ResolvedOperator& r, Expression e) { + auto x = r._clone().as(); + x.setOp2(std::move(e)); + return x; +} + +} // namespace resolved_operator +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/ternary.h b/hilti/include/ast/expressions/ternary.h new file mode 100644 index 000000000..c8c056fc1 --- /dev/null +++ b/hilti/include/ast/expressions/ternary.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace expression { + +/** AST node for a ternary expression. */ +class Ternary : public NodeBase, public trait::isExpression { +public: + Ternary(Expression cond, Expression true_, Expression false_, Meta m = Meta()) + : NodeBase({std::move(cond), std::move(true_), std::move(false_)}, std::move(m)) {} + + auto condition() const { return child(0); } + auto true_() const { return child(1); } + auto false_() const { return child(2); } + + bool operator==(const Ternary& other) const { + return condition() == other.condition() && true_() == other.true_() && false_() == other.false_(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true_().isTemporary() || false_().isTemporary(); } + /** Implements `Expression` interface. */ + Type type() const { + return true_().type(); + } // TODO(robin): Currentluy we enforce both having the same type; we might need to coerce to target type though + /** Implements `Expression` interface. */ + auto isConstant() const { return false; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/type-wrapped.h b/hilti/include/ast/expressions/type-wrapped.h new file mode 100644 index 000000000..e91ce3e93 --- /dev/null +++ b/hilti/include/ast/expressions/type-wrapped.h @@ -0,0 +1,102 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +namespace hilti { +namespace expression { + +/** + * AST node for an expression wrapped into another which does not have a + * known type yet, for example because IDs are stil unresolved. With a + * "normal" expression, calling `type()` would yield an unusable type. This + * expression instead returns a place-holder type that's derived on one of + * two ways: + * + * 1. If the fully resolved type of the expression is actually known + * a-priori, it can be jsut passed into the constructor and will then + * always be returned, independent of the inner expression's type + * itself. + * + * 2. If no explicit type is given, `type()` returns a proxy type that + * evaluates the expression's type on demand once requested (but not, + * crucially, immediately). So once the expression is fully resolved, + * this will yield its correct type. In the meantime, the proxy can be + * passed around like any other type. + * + * In case 1, one can in addition require that the expression's eventual + * fully-resolved type matches the type that was specified. If it doesn't the + * validator will then reject the code. + * + */ +class TypeWrapped : public NodeBase, public trait::isExpression { +public: + struct ValidateTypeMatch {}; + + TypeWrapped(Expression e, Meta m = Meta()) : NodeBase(nodes(std::move(e), node::none), std::move(m)) {} + + TypeWrapped(Expression e, bool change_constness_to, Meta m = Meta()) + : NodeBase(nodes(std::move(e), node::none), std::move(m)), _change_constness_to(change_constness_to) {} + + TypeWrapped(Expression e, Type t, Meta m = Meta()) : NodeBase(nodes(std::move(e), std::move(t)), std::move(m)) {} + + TypeWrapped(Expression e, Type t, ValidateTypeMatch _, Meta m = Meta()) + : NodeBase(nodes(std::move(e), std::move(t)), std::move(m)), _validate_type_match(true) {} + + TypeWrapped(Expression e, NodeRef t, Meta m = Meta()) + : NodeBase(nodes(std::move(e)), std::move(m)), _type_node_ref(std::move(t)) {} + + TypeWrapped(Expression e, NodeRef t, ValidateTypeMatch _, Meta m = Meta()) + : NodeBase(nodes(std::move(e)), std::move(m)), _validate_type_match(true), _type_node_ref(std::move(t)) {} + + auto expression() const { return child(0); } + bool validateTypeMatch() const { return _validate_type_match; } + + bool operator==(const TypeWrapped& other) const { + return expression() == other.expression() && type() == other.type(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { return expression().isLhs(); } + /** Implements `Expression` interface. */ + bool isTemporary() const { return expression().isTemporary(); } + /** Implements `Expression` interface. */ + Type type() const { + if ( _type_node_ref ) + return _type_node_ref->template as(); + + if ( auto t = childs()[1].tryAs() ) { + if ( t->template isA() ) + // Don't call effectiveType() here, we want to keep + // evaluation pending. + return *t; + + return type::effectiveType(*t); + } + + if ( _change_constness_to.has_value() ) + return type::Computed(expression(), *_change_constness_to, meta()); + + return type::Computed(expression(), meta()); + } + + /** Implements `Expression` interface. */ + auto isConstant() const { return expression().isConstant(); } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"validate_type_match", _validate_type_match}}; } + +private: + std::optional _change_constness_to; + bool _validate_type_match = false; + NodeRef _type_node_ref; +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/type.h b/hilti/include/ast/expressions/type.h new file mode 100644 index 000000000..26fc45587 --- /dev/null +++ b/hilti/include/ast/expressions/type.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a type expression. */ +class Type_ : public NodeBase, public trait::isExpression { +public: + Type_(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + + auto typeValue() const { return type::effectiveType(child(0)); } + + bool operator==(const Type_& other) const { return typeValue() == other.typeValue(); } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return type::Type_(child(0)); } + /** Implements `Expression` interface. */ + auto isConstant() const { return true; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/unresolved-operator.h b/hilti/include/ast/expressions/unresolved-operator.h new file mode 100644 index 000000000..b5e1d009f --- /dev/null +++ b/hilti/include/ast/expressions/unresolved-operator.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for an expression representing an unresolved operator usage. */ +class UnresolvedOperator : public NodeBase, public trait::isExpression { +public: + UnresolvedOperator(operator_::Kind op, std::vector operands, Meta meta = Meta()) + : NodeBase(nodes(std::move(operands)), std::move(meta)), _kind(op) {} + + auto kind() const { return _kind; } + + /** Implements interfave for use with `OverloadRegistry`. */ + auto operands() const { return childs(0, -1); } + + bool operator==(const UnresolvedOperator& other) const { + return kind() == other.kind() && operands() == other.operands(); + } + + /** Implements `Expression` interface. */ + bool isLhs() const { logger().internalError("UnresolvedOperator::isLhs() not supported"); } + /** Implements `Expression` interface. */ + bool isTemporary() const { logger().internalError("UnresolvedOperator::isTemporary() not supported"); } + /** Implements `Expression` interface. */ + auto type() const { return type::unknown; } + /** Implements `Expression` interface. */ + auto isConstant() const { return false; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{{"kind", to_string(_kind)}}; } + +private: + operator_::Kind _kind; +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/expressions/void.h b/hilti/include/ast/expressions/void.h new file mode 100644 index 000000000..cd0165223 --- /dev/null +++ b/hilti/include/ast/expressions/void.h @@ -0,0 +1,34 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace expression { + +/** AST node for a void expression. */ +class Void : public NodeBase, public hilti::trait::isExpression { +public: + Void(Meta m = Meta()) : NodeBase(std::move(m)) {} + + bool operator==(const Void& /* other */) const { return true; } + + /** Implements `Expression` interface. */ + bool isLhs() const { return false; } + /** Implements `Expression` interface. */ + bool isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto type() const { return type::Void(); } + /** Implements `Expression` interface. */ + auto isConstant() const { return true; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace expression +} // namespace hilti diff --git a/hilti/include/ast/function.h b/hilti/include/ast/function.h new file mode 100644 index 000000000..67bd482f9 --- /dev/null +++ b/hilti/include/ast/function.h @@ -0,0 +1,85 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace hilti { + +namespace function { + +/** A function's calling convention. */ +enum class CallingConvention { + Extern, /**< function can be called from external C++ code */ + Standard /**< default, nothing special */ +}; + +namespace detail { +constexpr util::enum_::Value conventions[] = { + {CallingConvention::Extern, "extern"}, + {CallingConvention::Standard, ""}, +}; +} // namespace detail + +constexpr auto to_string(CallingConvention cc) { return util::enum_::to_string(cc, detail::conventions); } + +namespace calling_convention { +constexpr inline auto from_string(const std::string_view& s) { + return util::enum_::from_string(s, detail::conventions); +} +} // namespace calling_convention + +} // namespace function + +/** AST node representing a function. */ +class Function : public NodeBase { +public: + Function(ID id, Type type, std::optional body, + function::CallingConvention cc = function::CallingConvention::Standard, + std::optional attrs = {}, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), std::move(body), std::move(attrs)), std::move(m)), _cc(cc) {} + + Function() : NodeBase(nodes(node::none, node::none, node::none, node::none), Meta()) {} + + auto id() const { return child(0); } + auto type() const { return type::effectiveType(child(1)).as(); } + auto body() const { return childs()[2].tryAs(); } + auto attributes() const { return childs()[3].tryAs(); } + auto callingConvention() const { return _cc; } + auto isStatic() const { return AttributeSet::find(attributes(), "&static"); } + + bool operator==(const Function& other) const { + return id() == other.id() && type() == other.type() && body() == other.body() && + attributes() == other.attributes() && callingConvention() == other.callingConvention(); + } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"cc", to_string(_cc)}}; } + + /** + * Returns a new funnction with the body replaced. + * + * @param d original function + * @param b new body + * @return new function that's equal to original one but with the body replaced + */ + static Function setBody(const Function& d, const Statement& b) { + auto x = Function(d); + x.childs()[2] = b; + return x; + } + +private: + function::CallingConvention _cc = function::CallingConvention::Standard; +}; + +/** Creates an AST node representing a `Function`. */ +inline Node to_node(Function f) { return Node(std::move(f)); } + +} // namespace hilti diff --git a/hilti/include/ast/id.h b/hilti/include/ast/id.h new file mode 100644 index 000000000..f0f0ffc11 --- /dev/null +++ b/hilti/include/ast/id.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace hilti { + +/** AST node representing an identifier. */ +class ID : public detail::IDBase, public NodeBase { +public: + using detail::IDBase::IDBase; + ID(std::string name, Meta m) : IDBase(std::move(name)), NodeBase(std::move(m)) {} + ID() = default; + + /** Assignment from string without changing location information. */ + ID& operator=(const std::string& s) { + IDBase::operator=(ID(s)); + return *this; + } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"name", std::string(*this)}}; } +}; + +inline std::ostream& operator<<(std::ostream& out, const ID& id) { + out << std::string(id); + return out; +} + +/** Creates an AST node representing a `ID`. */ +inline Node to_node(ID i) { return Node(std::move(i)); } + +} // namespace hilti + +namespace std { +template<> +struct hash { + std::size_t operator()(const hilti::ID& id) const { return hash()(id); } +}; +} // namespace std diff --git a/hilti/include/ast/location.h b/hilti/include/ast/location.h new file mode 100644 index 000000000..9e5262be3 --- /dev/null +++ b/hilti/include/ast/location.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace hilti { + +/** + * Source code locations associated with AST nodes. + */ +class Location { +public: + /** + * Constructor. If all arguments are left at their default, the instance + * will match `location::None`. + * + * @param file file name/path associated with the location; empty if unknown. + * @param from first line number of the described range; -1 if not availabl. + * @param to last line number of the described range; -1 if not availabl. + */ + Location(std::filesystem::path file = "", int from = -1, int to = -1) + : _file(std::move(file)), _from(from), _to(to) {} + + Location(const Location&) = default; + Location(Location&&) = default; + Location& operator=(const Location&) = default; + Location& operator=(Location&&) = default; + ~Location() = default; + + auto file() const { return _file.generic_string(); } + auto from() const { return _from; } + auto to() const { return _to; } + + /** + * Returns a string representation of the location. + * + * @param no_path if true, do not include the file + */ + std::string render(bool no_path = false) const; + + /** + * Returns true if the location is set. A location is unset if it equals + * `location::None` (which a default constructed location will).. + */ + explicit operator bool() const; + + /** Forwards to `render()`. */ + operator std::string() const { return render(); } + +private: + std::filesystem::path _file; + int _from; + int _to; +}; + +/** Forwards to `Location::render()`. */ +inline auto to_string(const Location& l) { return l.render(); } + +/** Forwards to `Location::render()`. */ +inline std::ostream& operator<<(std::ostream& out, const Location& l) { + out << l.render(); + return out; +} + +namespace location { +/** Sentinel value indicating that no location information is available. */ +extern const Location None; +} // namespace location + +} // namespace hilti diff --git a/hilti/include/ast/meta.h b/hilti/include/ast/meta.h new file mode 100644 index 000000000..47958669c --- /dev/null +++ b/hilti/include/ast/meta.h @@ -0,0 +1,43 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti { + +/** + * Meta information associated with AST nodes. The meta data can include a + * source code location, source code comments, and an error message. + */ +class Meta { +public: + /** List of comments. */ + using Comments = std::vector; + + Meta(Location location, Comments comments = {}) : _location(std::move(location)), _comments(std::move(comments)) {} + + /** Constructor that leaves location unset. */ + Meta(Comments comments = {}) : _comments(std::move(comments)) {} + + const Location& location() const { return _location; } + const Comments& comments() const { return _comments; } + + void setLocation(const Location& l) { _location = l; } + void setComments(const Comments& c) { _comments = c; } + + /** + * Returns true if the location does not equal a default constructed + * instance. + */ + explicit operator bool() const { return _location || _comments.size(); } + +private: + Location _location; + Comments _comments; +}; + +} // namespace hilti diff --git a/hilti/include/ast/module.h b/hilti/include/ast/module.h new file mode 100644 index 000000000..cb5cfd713 --- /dev/null +++ b/hilti/include/ast/module.h @@ -0,0 +1,109 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +/** AST node representing a HILTI module. */ +class Module : public NodeBase { +public: + Module(ID id = {}, Meta m = Meta()) : NodeBase({std::move(id), statement::Block({}, m)}, std::move(m)) {} + + Module(ID id, std::vector decls, Meta m = Meta()) + : NodeBase(nodes(std::move(id), statement::Block({}, m), std::move(decls)), std::move(m)) {} + + Module(ID id, std::vector decls, std::vector stmts, const Meta& m = Meta()) + : NodeBase(nodes(std::move(id), statement::Block(std::move(stmts), m), std::move(decls)), m) {} + + auto id() const { return child(0); } + auto statements() const { return child(1); } + auto declarations() const { return childs(2, -1); } + const auto& preserved() const { return _preserved; } + + bool isEmpty() const { + // We always have an ID and a block as childs. + return childs().size() <= 2 && statements().statements().empty(); + } + + /** + * Returns a module's property declaration of a given name. If there's no + * property declaration of that name, return an error. If there's more than + * one of that name, it's undefined which one is returned. + * + * @param id name of the property to return + */ + Result moduleProperty(const ID& id) const; + + /** + * Returns all of module's property declarations of a given name. If + * there's no property declaration of that ID, return an an empty container. + * + * @param id name of the property to return + */ + std::vector moduleProperties(const ID& id) const; + + /** + * Adds a declaration to the module. It will be appended to the current + * list of declarations. + * + * Note this is a mutating function. `Module` is an exception among AST + * classes in that we allow to modify existing instances. Changes will be + * reflected in all copies of this instance. + */ + void add(Declaration n) { addChild(std::move(n)); } + + /** + * Adds a top-level statement to the module. It will be appended to the + * end of the current list of statements and execute at module initialize + * time. + * + * Note this is a mutating function. `Module` is an exception among AST + * classes in that we allow to modify existing instances. Changes will be + * reflected in all copies of this instance. + * + */ + void add(Statement s) { childs()[1].as()._add(std::move(s)); } + + /** + * Adds a top-level expression to the module. It will be appended to the + * end of the current list of statements and be evaluated at module + * initialize time. + * + * Note this is a mutating function. `Module` is an exception among AST + * classes in that we allow to modify existing instances. Changes will be + * reflected in all copies of this instance. + * + */ + void add(Expression e) { add(statement::Expression(std::move(e))); } + + /** + * Saves a node along with the module, but outside of the actual AST. + * This allows keeping references to the node valid while not making the + * node itself part of the AST. That's especially useful when + * transforming nodes from one representation to another, but wanting to + * retain a link to the original one through `Node::setOriginalNode()`. + * + * @return reference to the preserved node + */ + NodeRef preserve(Node n); + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + std::vector _preserved; +}; + +/** Creates an AST node representing a `Module`. */ +inline Node to_node(Module i) { return Node(std::move(i)); } + +} // namespace hilti diff --git a/hilti/include/ast/node.api b/hilti/include/ast/node.api new file mode 100644 index 000000000..e94c5036b --- /dev/null +++ b/hilti/include/ast/node.api @@ -0,0 +1,29 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for all AST nodes. */ +class Node(trait::isNode) { + /** Returns any properties associated with the the node. */ + hilti::node::Properties properties() const; + + /** Returns the node's childrens. */ + const std::vector& childs() const; + + /** Returns the node's childrens. */ + std::vector& childs(); + + /** Returns the node's meta data. */ + const Meta& meta() const; + + /** Sets the node's meta data. */ + void setMeta(Meta m); + + /** + * Returns an original, pre-transformation node associated with the node + * if such has been set. + */ + const NodeRef& originalNode() const; + + /** Associates an original, pre-transformation node with this node. */ + void setOriginalNode(const NodeRef& n); + +}; diff --git a/hilti/include/ast/node.h b/hilti/include/ast/node.h new file mode 100644 index 000000000..9efcf8084 --- /dev/null +++ b/hilti/include/ast/node.h @@ -0,0 +1,577 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace trait { +class isNode {}; +} // namespace trait +} // namespace hilti + +#include +#include +#include +#include +#include + +namespace hilti { + +class Node; +class NodeRef; + +namespace node { +namespace detail { + +/** Value of a node property, stored as part of `Properties`. */ +using PropertyValue = std::variant; + +/** Renders a property value into a string for display. */ +inline std::string to_string(PropertyValue v) { + struct Visitor { + auto operator()(bool s) { return std::string(s ? "true" : "false"); } + auto operator()(const char* s) { return std::string(s); } + auto operator()(double d) { return util::fmt("%.6f", d); } + auto operator()(int i) { return util::fmt("%d", i); } + auto operator()(int64_t i) { return util::fmt("%" PRId64, i); } + auto operator()(std::string s) { return s; } + auto operator()(unsigned int u) { return util::fmt("%u", u); } + auto operator()(uint64_t u) { return util::fmt("%" PRIu64, u); } + }; + + return std::visit(Visitor(), v); +}; + +/** Internal error information associated with nodes. */ +struct Error { + std::string message; + std::vector context; +}; + +} // namespace detail + +/** + * Properties associated with an AST node. A property is a key/value pair + * recording node-specific, atomic information that's not represented by + * further child nodes. + */ +using Properties = std::map; + +namespace detail { +#include + +} // namespace detail +} // namespace node + +/** + * AST node. This is a type-erased class that wraps all AST nodes. + * + * @note Do not derive from this class. Derive from `NodeBase` instead and + * then implement the `Node` interface. + */ +class Node final : public node::detail::Node { +public: + /** Constructs a node from an instance of a class implementing the `Node` interface. */ + template::value>* = nullptr> + Node(T t) : node::detail::Node(std::move(t)) {} + + Node(const Node& other) : node::detail::Node::Node(other), _scope(other._scope) {} + Node(Node&& other) noexcept + : node::detail::Node::Node(std::move(other)), + _control_ptr(std::move(other._control_ptr)), + _scope(std::move(other._scope)) { + if ( _control_ptr ) + _control_ptr->_node = this; + } + + Node() = delete; + + explicit Node(std::shared_ptr data) : node::detail::Node(std::move(data)) {} + + ~Node() final { + if ( _control_ptr ) + _control_ptr->_node = nullptr; + } + + /** + * Returns the node's unique control ID if there's at least `NodeRef` has + * been created that refers to it. If there's no such NodeRef, returns + * zero. + * + * @note This is primarily for internal usage. + */ + uint64_t rid() const { return _control_ptr ? _control_ptr->_rid : 0; } + + /** + * Returns a string representation of `rid()`. + * + * @note This is primarily for internal usage. + */ + std::string renderedRid() const { return rid() ? util::fmt("%%%" PRIu64, rid()) : "%???"; }; + + /** + * Returns the scope associated with the node. All nodes have a scope + * used for ID resolution. Initially, a new node receive its own, empty + * scope. However, scopes can be shared across nodes through + * `setScope()`. + */ + std::shared_ptr scope() const { + if ( ! _scope ) + _scope = std::make_shared(); + + return _scope; + } + + /** + * Resets the node's scope to point to another one. Nodes + */ + void setScope(std::shared_ptr new_scope) { _scope = std::move(new_scope); } + + /** Returns an error message associated with the node, if any. */ + std::optional error() const { + if ( _error ) + return _error->message; + else + return {}; + } + + /** Associated an error message with the node. */ + void setError(std::string msg) { + if ( ! _error ) + _error = std::make_unique(); + + _error->message = std::move(msg); + _error->context.clear(); + } + + /** Clears any error message associated with the node. */ + void clearError() { _error.reset(); } + + /** Adds a line of free-form context to the current error. */ + void augmentError(std::string s) { + if ( ! _error ) + _error = std::make_unique(); + + _error->context.emplace_back(std::move(s)); + } + + /** Returns any context currently associated with the current error. */ + std::vector errorContext() const { + if ( _error ) + return _error->context; + else + return {}; + } + + /** + * Returns an internal string representation of the node and all its + * children. + * + * @param include_location if true, include source code locations into + * the output + */ + std::string render(bool include_location = true) const; + + /** + * Returns a HILTI source code representation of the node and all its + * children. If the node is not the root of an AST, it's not guaranteed + * that the result will form valid HILTI source code (but it can still be + * used, e.g., in error messages). + * + * @param compact create a one-line representation + * + */ + void print(std::ostream& out, bool compact = false) const; + + /** Convenience method to return the meta data's location information. */ + const Location& location() const { return meta().location(); } + + /** Aborts execution if node is not of a given type `T`. */ + template + void assertIsA() { + if ( ! isA() ) { + std::cerr << "Assertion failure: Node expected to be a " << typeid(T).name() << " but is a " + << typeid_().name() << std::endl; + util::abort_with_backtrace(); + } + } + + /** Renders the node as HILTI source code. */ + operator std::string() const { + std::stringstream buf; + print(buf, true); + return buf.str(); + } + + /** + * Replaces the node with another one. Existing `NodeRef` pointing to + * this node will remain valid and reflect the new value. + */ + Node& operator=(const Node& n) { + _scope = n._scope; + node::detail::ErasedBase::operator=(n); + return *this; + } + + /** + * Replaces the node with another one. Existing `NodeRef` pointing to + * this node will remain valid and reflect the new value. + */ + Node& operator=(Node&& n) noexcept { + _scope = std::move(n._scope); + node::detail::ErasedBase::operator=(std::move(n)); + return *this; + } + + /** + * Replaces the node with an instance of a class implementing the `Node` + * interface. Existing `NodeRef` pointing to this node will remain valid + * and reflect the new value. + */ + template + Node& operator=(const T& t) { + node::detail::ErasedBase::operator=(to_node(t)); + return *this; + } + +private: + friend class NodeRef; + + // Returns (and potentially) created the control block for this node that + // NodeRef uses to maintain links to it. + std::shared_ptr _control() { + if ( ! _control_ptr ) + _control_ptr = std::make_shared(this); + + return _control_ptr; + } + + std::shared_ptr _control_ptr = nullptr; + mutable std::shared_ptr _scope = nullptr; + mutable std::unique_ptr _error = nullptr; +}; + +/** + * Common base class for classes implementing the `Node` interface. The base + * implements a number of the interface methods with standard versions shared + * across all nodes. + */ +class NodeBase : public trait::isNode { +public: + /** + * Constructor. + * + * @param meta meta information to associate with the node + */ + NodeBase(Meta meta) : _meta(std::move(meta)) {} + + /** + * Constructor registering child nodes. + * + * @param childs children of this node + * @param meta meta information to associate with the node + */ + NodeBase(std::vector childs, Meta meta) : _meta(std::move(meta)) { + for ( auto& c : childs ) + addChild(std::move(c)); + } + + NodeBase() = default; + + /** + * Returns a child. + * + * @tparam T type that the child nodes are assumed to (and must) have + * @param i index of the child, in the order they were passed into the constructor and/or added + * @return child casted to type `T` + */ + template + const T& child(int i) const { + return _childs[i].as(); + } + + /** + * Aborts execution if a given child is not an expected type `T`. + * + * @tparam T type that the child node is assumed to have + * @param i index of the child, in the order they were passed into the constructor and/or added + */ + template + void assertChildIsA(int i) { + _childs[i].template assertIsA(); + } + + /** + * Returns a subrange of children. The indices correspond to the order + * children were passed into the constructor and/or added. + * + * @tparam T type that the child nodes are assumed to (and must) have + * @param begin index of first child to include; a negative index counts Python-style from end of list + * @param end index of one beyond last child to include; a negative index counts Python-style from end of list + * @return childs from `start` to `end` + */ + template + std::vector childs(int begin, int end) const { + std::vector n; + + if ( end < 0 ) + end = _childs.size(); + + for ( auto i = begin; i < end; i++ ) + n.emplace_back(_childs[i].as()); + + return n; + } + + /** + * Returns a subset of children by type. + * + * @tparam T type of children to return + * @return all childs that have type `T` + */ + template + std::vector childsOfType() const { + std::vector n; + for ( auto& c : _childs ) { + if ( auto x = c.tryAs() ) + n.emplace_back(*x); + } + + return n; + } + + /** + * Returns a subset of children `Node` references, selected by type. + * + * @tparam T type of children to return + * @return all childs that have type `T` + */ + template + auto nodesOfType() const { + std::vector> n; + for ( const auto& c : _childs ) { + if ( c.isA() ) + n.emplace_back(c); + } + + return n; + } + + template + auto nodesOfType() { + std::vector> n; + for ( auto& c : _childs ) { + if ( c.isA() ) + n.emplace_back(c); + } + + return n; + } + + /** + * Adds a child node. It will be appended to the end of the current list + * node of children. + */ + void addChild(Node n) { + if ( _meta.location() && ! n.location() ) { + auto m = n.meta(); + m.setLocation(_meta.location()); + n.setMeta(std::move(m)); + } + + _childs.push_back(std::move(n)); + } + + /** Implements the `Node` interface. */ + auto& childs() const { return _childs; } + /** Implements the `Node` interface. */ + auto& childs() { return _childs; } + /** Implements the `Node` interface. */ + auto& meta() const { return _meta; } + /** Implements the `Node` interface. */ + void setMeta(Meta m) { _meta = std::move(m); } + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const { return _orig; } + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n) { _orig = n; } + +private: + std::vector<::hilti::Node> _childs; + Meta _meta; + NodeRef _orig; +}; + +namespace node { + +/** Place-holder node for an optional node that's not set. */ +class None : public NodeBase, public util::type_erasure::trait::Singleton { +public: + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Wrapper around constructor so that we can make it private. Don't use + * this, use the singleton `type::unknown` instead. + */ + static None create() { return None(); } + +private: + None() : NodeBase(Meta()) {} +}; + +/** Singleton. */ +static const Node none = None::create(); + +} // namespace node + +inline const Node& to_node(const node::None& /* n */) { return node::none; } + +/** + * No-op function implementing the `to_node` API for instances that already + * are of type `Node`. + */ +template // Don't allow derived classes. +inline Node to_node(const T& n) { + return n; +} + +/** Implements the `to_node` API for optional nodes. */ +template +Node to_node(std::optional t) { + if ( t ) + return to_node(std::move(*t)); + + return to_node(node::none); +} + +/** + * Creates `Node` instances for a vector of objects all implementing the + * `Node` interface. + */ +template +std::vector nodes(std::vector t) { + std::vector v; + v.reserve(t.size()); + for ( const auto& i : t ) + v.emplace_back(std::move(i)); + return v; +} + +/** + * Creates `Node` instances for a list of objects all implementing the + * `Node` interface. + */ +template +std::vector nodes(std::list t) { + std::vector v; + for ( const auto& i : t ) + v.emplace_back(std::move(i)); + return v; +} + +/** + * Creates `Node` instances for a set of objects all implementing the `Node` + * interface. + */ +template +std::vector nodes(std::set t) { + std::vector v; + v.reserve(t.size()); + for ( const auto& i : t ) + v.emplace_back(std::move(i)); + return v; +} + +/** + * Creates `Node` instances for a vector of pairs of objects all implementing + * the `Node` interface. The pair will be flattened in the result. + */ +template +std::vector nodes(std::vector> t) { + std::vector v; + v.reserve(t.size() * 2); + for ( const auto& i : t ) { + v.emplace_back(std::move(i.first)); + v.emplace_back(std::move(i.second)); + } + return v; +} + +/** Create a 1-element vector of nodes for an object implementing the `Node` API. */ +template +std::vector nodes(T t) { + return {to_node(std::move(t))}; +} + +/** + * Creates `Node` instances for objects all implementing the `Node` + * interface. + */ +template +std::vector nodes(T t, Ts... ts) { + return util::concat(nodes(t), nodes(ts...)); +} + +/** + * Checks equality for two objects both implementing the `Node` interface. + * + * If the two objects have different types, this will return false. Otherwise + * it will forward to the objects equality operator. + */ +namespace node { +template +bool isEqual(const T* this_, const Other& other) { + if ( auto o = other.template tryAs() ) + return *this_ == *o; + + return false; +} + +} // namespace node + +/** Renders a node as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, const Node& n) { + n.print(out, true); + return out; +} + +namespace node { +namespace detail { +// Backend to NodeBase::flattenedChilds. +template +void flattenedChilds(const Node& n, std::vector* dst) { + for ( const auto& c : n.childs() ) { + if ( auto t = c.tryAs() ) + dst->push_back(*t); + + flattenedChilds(c, dst); + } +} + +} // namespace detail + +/** + * Returns a list of all childs of specific type, descending recursively + * to find instance anywhere below this node. + */ +template +std::vector flattenedChilds(const Node& n) { + std::vector dst; + detail::flattenedChilds(n, &dst); + return dst; +} + +} // namespace node + +} // namespace hilti + +extern hilti::node::Properties operator+(const hilti::node::Properties& p1, const hilti::node::Properties& p2); diff --git a/hilti/include/ast/node_ref.h b/hilti/include/ast/node_ref.h new file mode 100644 index 000000000..bb171d792 --- /dev/null +++ b/hilti/include/ast/node_ref.h @@ -0,0 +1,92 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace hilti { + +class Node; + +namespace node_ref { +namespace detail { + +// Control block for refering to nodes. +class Control { +public: + Control(Node* n) : _node(n), _rid(++_rid_counter) {} + Node* _node; + uint64_t _rid; + + static uint64_t _rid_counter; +}; + +} // namespace detail + +/** Exceptions thrown when a dangling `NodeRef` is deferenced. */ +struct Invalid : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +} // namespace node_ref + +/** + * A reference to an AST `Node`. A reference acts similarly to a pointer to a + * node, except that it tracks its life-time and catches (through exceptions) + * if the reference becomes invalid. Assigning to a node doesn't affect any + * references to it; they will now point to the new value. + */ +class NodeRef { +public: + explicit NodeRef(Node& n); // NOLINT + explicit NodeRef(const NodeRef& other) = default; + NodeRef(NodeRef&& other) = default; + NodeRef& operator=(const NodeRef& other) = default; + NodeRef& operator=(NodeRef&& other) = default; + NodeRef() = default; + ~NodeRef() = default; + + /** + * Returns the referenced node's unique control ID, or 0 if the instance + * isn't referencing anything. + * + * @note This is primarily for internal usage. + */ + uint64_t rid() const { return _control ? _control->_rid : 0; } + + /** + * Returns a string version of the referenced node's unique control ID, + * or 0 if the instance isn't referencing anything. + * + * @note This is primarily for internal usage. + */ + std::string renderedRid() const { return rid() ? util::fmt("%%%" PRIu64, rid()) : "%???"; }; + + /** + * Returns a pointer to the the referenced node. + * + * @exception Invalid if the node does not exist anymore + */ + const Node* operator->() const { return _node(); } + + /** + * Returns a direct C++ reference to the the referenced node. The node + * may be modified, and assigned to, through this reference. + * + * @exception Invalid if the node does not exist anymore + */ + Node& operator*() const { return *_node(); } + + operator const Node&() const { return *_node(); } + + /** Returns true if the instance references a valid node. */ + explicit operator bool() const { return _control && _control->_node; } + +private: + Node* _node() const; + std::shared_ptr _control; +}; + +} // namespace hilti diff --git a/hilti/include/ast/nodes.decl b/hilti/include/ast/nodes.decl new file mode 100644 index 000000000..053ad3840 --- /dev/null +++ b/hilti/include/ast/nodes.decl @@ -0,0 +1,155 @@ + +trait hilti::Declaration isDeclaration +trait hilti::Node isNode +trait hilti::Type isType +trait hilti::Statement isStatement +trait hilti::Expression isExpression +trait hilti::Ctor isCtor +trait hilti::expression::ResolvedOperator isResolvedOperator + +hilti::Attribute : isNode +hilti::AttributeSet : isNode +hilti::Ctor : isNode +hilti::Declaration : isNode +hilti::Expression : isNode +hilti::Function : isNode +hilti::ID : isNode +hilti::Module : isNode +hilti::Statement : isNode +hilti::Type : isNode +hilti::type::function::Result : isNode +hilti::type::function::Parameter : isNode +hilti::type::union_::Field : isNode +hilti::type::struct_::Field : isNode +hilti::statement::switch_::Case : isNode +hilti::statement::try_::Catch : isNode + +hilti::ctor::Address : isCtor +hilti::ctor::Bool : isCtor +hilti::ctor::Bytes : isCtor +hilti::ctor::Coerced : isCtor +hilti::ctor::Default : isCtor +hilti::ctor::Enum : isCtor +hilti::ctor::Error : isCtor +hilti::ctor::Exception : isCtor +hilti::ctor::Interval : isCtor +hilti::ctor::List : isCtor +hilti::ctor::Map : isCtor +hilti::ctor::Network : isCtor +hilti::ctor::Null : isCtor +hilti::ctor::Optional : isCtor +hilti::ctor::Port : isCtor +hilti::ctor::Real : isCtor +hilti::ctor::RegExp : isCtor +hilti::ctor::Result : isCtor +hilti::ctor::Set : isCtor +hilti::ctor::SignedInteger : isCtor +hilti::ctor::Stream : isCtor +hilti::ctor::String : isCtor +hilti::ctor::StrongReference : isCtor +hilti::ctor::Struct : isCtor +hilti::ctor::Time : isCtor +hilti::ctor::Tuple : isCtor +hilti::ctor::UnsignedInteger : isCtor +hilti::ctor::ValueReference : isCtor +hilti::ctor::Vector : isCtor +hilti::ctor::WeakReference : isCtor +hilti::ctor::ValueReference : isCtor + +hilti::declaration::Constant : isDeclaration +hilti::declaration::Expression : isDeclaration +hilti::declaration::Forward : isDeclaration +hilti::declaration::Function : isDeclaration +hilti::declaration::GlobalVariable : isDeclaration +hilti::declaration::ImportedModule : isDeclaration +hilti::declaration::LocalVariable : isDeclaration +hilti::declaration::Parameter : isDeclaration +hilti::declaration::Property : isDeclaration +hilti::declaration::Type : isDeclaration + +hilti::expression::Assign : isExpression +hilti::expression::Coerced : isExpression +hilti::expression::PendingCoerced : isExpression +hilti::expression::Ctor : isExpression +hilti::expression::Deferred : isExpression +hilti::expression::Keyword : isExpression +hilti::expression::ListComprehension : isExpression +hilti::expression::LogicalAnd : isExpression +hilti::expression::LogicalOr : isExpression +hilti::expression::LogicalNot : isExpression +hilti::expression::Member : isExpression +hilti::expression::Move : isExpression +hilti::expression::ResolvedID : isExpression +hilti::expression::ResolvedOperator : isExpression +hilti::expression::Ternary : isExpression +hilti::expression::Type_ : isExpression +hilti::expression::TypeWrapped : isExpression +hilti::expression::UnresolvedID : isExpression +hilti::expression::UnresolvedOperator : isExpression +hilti::expression::Void : isExpression + +hilti::statement::Assert : isStatement +hilti::statement::Block : isStatement +hilti::statement::Break : isStatement +hilti::statement::Comment : isStatement +hilti::statement::Continue : isStatement +hilti::statement::Declaration : isStatement +hilti::statement::Expression : isStatement +hilti::statement::For : isStatement +hilti::statement::If : isStatement +hilti::statement::Return : isStatement +hilti::statement::Switch : isStatement +hilti::statement::Throw : isStatement +hilti::statement::Try : isStatement +hilti::statement::While : isStatement +hilti::statement::Yield : isStatement + +hilti::type::Address : isType +hilti::type::Any : isType +hilti::type::Bool : isType +hilti::type::Bytes : isType +hilti::type::Computed : isType +hilti::type::DocOnly : isType +hilti::type::Enum : isType +hilti::type::Error : isType +hilti::type::Exception : isType +hilti::type::Function : isType +hilti::type::Interval : isType +hilti::type::Library : isType +hilti::type::List : isType +hilti::type::Map : isType +hilti::type::Member : isType +hilti::type::Network : isType +hilti::type::Null : isType +hilti::type::OperandList : isType +hilti::type::Optional : isType +hilti::type::Port : isType +hilti::type::Real : isType +hilti::type::StrongReference : isType +hilti::type::RegExp : isType +hilti::type::ResolvedID : isType +hilti::type::Result : isType +hilti::type::Set : isType +hilti::type::SignedInteger : isType +hilti::type::Stream : isType +hilti::type::String : isType +hilti::type::Struct : isType +hilti::type::Time : isType +hilti::type::Tuple : isType +hilti::type::Type_ : isType +hilti::type::Union : isType +hilti::type::Unknown : isType +hilti::type::UnresolvedID : isType +hilti::type::UnsignedInteger : isType +hilti::type::Vector : isType +hilti::type::Void : isType +hilti::type::WeakReference : isType +hilti::type::ValueReference : isType +hilti::type::bytes::Iterator : isType +hilti::type::enum_::Label : isType +hilti::type::list::Iterator : isType +hilti::type::map::Iterator : isType +hilti::type::set::Iterator : isType +hilti::type::stream::Iterator : isType +hilti::type::stream::View : isType +hilti::type::vector::Iterator : isType diff --git a/hilti/include/ast/operator.api b/hilti/include/ast/operator.api new file mode 100644 index 000000000..04d79358e --- /dev/null +++ b/hilti/include/ast/operator.api @@ -0,0 +1,74 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * Interface for HILTI operators. + * + * Operators aren't AST nodes themselves. Instead, they generally *define* an + * operator that's available in HILTI. An operator can then instantiated with + * concrete operands to create a corresponding AST expression node. + */ +class Operator(trait::isOperator) { + /** Returns the operator's kind. */ + hilti::operator_::Kind kind() const; + + /** + * Returns a documentation string associated with the operatior. This + * goes into automatically generated HILTI reference documentation. + */ + std::string doc() const; + + /** + * Returns the internal namespace this operator is defined in. This is + * used for grouping operators in the auto-generated documentation. + */ + std::string docNamespace() const; + + /** + * Validates correctness of an instantiated version of this operator. + * This will be called as part of HILTI's AST validation process and + * should ensure that the operator's operands are valid. + * + * @param o instantiated operator expression + * @param p position of `o` in the AST being validated; can be used to derive further context about the usage + * @return false if the instantiation doesn't reflect a correct usage of the operator + */ + void validate(const expression::ResolvedOperator& o, operator_::const_position_t p) const; + + /** + * Returns the HILTI type that the operator yields when evaluated with a + * given set of operands. + * + * @param ops operands to use for deriving the result type; note that + * when auto-generating documentation, this vector will always be empty. + * Operators that require expressions to calculate their return type + * dynamically must catch getting an empty vector and return a + * type::DocOnly() instance in that case that provides a textual + * description for documentation. + * + * @return type when evaluated + * + */ + Type result(const std::vector& ops) const; + + /** True if operator's result can be assigned to. */ + bool isLhs() const; + + /** + * Describes the operands that the operator accepts, such as their types + * and default values. + * + * @return + */ + std::vector operands() const; + + /** + * Creates an expression representing this operator instantied with a + * given set of operands. The returned expression will typically be a + * `expression::ResolvedOperator`. + * + * @param ops operands to instantiate the operator with + * @param m meta information to associated with the instantiated expression + * @return new expression to integrate into AST + */ + Expression instantiate(const std::vector& operands, const Meta& m) const; +}; diff --git a/hilti/include/ast/operator.h b/hilti/include/ast/operator.h new file mode 100644 index 000000000..370715d3c --- /dev/null +++ b/hilti/include/ast/operator.h @@ -0,0 +1,387 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +namespace visitor {} + +namespace expression { +namespace resolved_operator { + +namespace detail { +class ResolvedOperator; +} // namespace detail +} // namespace resolved_operator + +using ResolvedOperator = resolved_operator::detail::ResolvedOperator; + +} // namespace expression + +namespace trait { +/** Trait for classes implementing the `Operator` interface. */ +class isOperator : public isNode {}; +} // namespace trait + +namespace expression { +class UnresolvedOperator; +} // namespace expression + +namespace operator_ { + +using position_t = visitor::Position; +using const_position_t = visitor::Position; + +using OperandType = + std::variant(const std::vector&, const std::vector&)>>; +inline std::optional type(const OperandType& t, const std::vector& orig_ops, + const std::vector& resolved_ops) { + if ( const auto& f = std::get_if< + std::function(const std::vector&, const std::vector&)>>(&t) ) + return (*f)(orig_ops, resolved_ops); + + return std::get(t); +} + +inline auto operandType(unsigned int op, const char* doc = "") { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) + logger().internalError(util::fmt("operandType(): index %d out of range, only %" PRIu64 " ops available", op, + resolved_ops.size())); + + return resolved_ops[op].type(); + }; +} + +inline auto elementType(unsigned int op, const char* doc = "", bool infer_const = true) { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) + logger().internalError(util::fmt("elementType(): index %d out of range, only %" PRIu64 " ops available", op, + resolved_ops.size())); + + if ( type::isIterable(resolved_ops[op].type()) ) { + auto t = resolved_ops[op].type().elementType(); + return (infer_const && resolved_ops[op].isConstant()) ? type::constant(t) : std::move(t); + } + + return {}; + }; +} + +inline auto constantElementType(unsigned int op, const char* doc = "") { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) + logger().internalError(util::fmt("elementType(): index %d out of range, only %" PRIu64 " ops available", op, + resolved_ops.size())); + + if ( type::isIterable(resolved_ops[op].type()) ) + return type::constant(resolved_ops[op].type().elementType()); + + return {}; + }; +} + +inline auto dereferencedType(unsigned int op, const char* doc = "", bool infer_const = true) { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) + logger().internalError(util::fmt("dereferencedType(): index %d out of range, only %" PRIu64 + " ops available", + op, resolved_ops.size())); + + if ( type::isDereferencable(resolved_ops[op].type()) ) { + auto t = resolved_ops[op].type().dereferencedType(); + return (infer_const && resolved_ops[op].isConstant()) ? type::constant(t) : std::move(t); + } + + return {}; + }; +} + +inline auto sameTypeAs(unsigned int op, const char* doc = "") { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) + logger().internalError(util::fmt("sameTypeAs(): index %d out of range, only %" PRIu64 " ops available", op, + resolved_ops.size())); + + return resolved_ops[op].type(); + }; +} + +inline auto typedType(unsigned int op, const char* doc = "") { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) + logger().internalError(util::fmt("typedType(): index %d out of range, only %" PRIu64 " ops available", op, + resolved_ops.size())); + + return type::effectiveType(resolved_ops[op].type().as().typeValue()); + }; +} + +/** Describes an operand that an operator accepts. */ +struct Operand { + std::optional id; /**< ID for the operand; used only for documentation purposes. */ + OperandType type; /**< operand's type */ + bool optional = false; /**< true if operand can be skipped; `default_` will be used instead */ + std::optional default_ = {}; /**< default valuer if operator is skipped */ + std::optional doc; /**< alternative rendering for the auto-generated documentation */ + + bool operator==(const Operand& other) const { + if ( this == &other ) + return true; + + if ( ! (std::holds_alternative(type) && std::holds_alternative(other.type)) ) + return false; + + return std::get(type) == std::get(other.type) && id == other.id && optional == other.optional && + default_ == other.default_; + } +}; + +inline std::ostream& operator<<(std::ostream& out, const Operand& op) { + if ( auto t = std::get_if(&op.type) ) + out << *t; + else + out << ""; + + if ( op.id ) + out << ' ' << *op.id; + + if ( op.default_ ) + out << " = " << *op.default_; + else if ( op.optional ) + out << " (optional)"; + + return out; +} + +using ResultType = OperandType; + +/** + * Describes the signature of an operator method. + * + * @todo For operands, we only use the type information so far. Instead of + * using `type::Tuple` to describe the 3rd parameter to a MethodCall + * operator, we should create a new `type::ArgumentList` that takes a list + * of `Operand` instances. + */ +struct Signature { + Type self; /**< type the method operates on */ + bool const_ = true; + bool lhs = false; /**< true if operator's result can be assigned to */ + ResultType result; /**< result of the method; skipped if using `{BEGIN/END}_METHOD_CUSTOM_RESULT}` */ + ID id; /**< name of the method */ + std::vector args; /**< operands the method receives */ + std::string doc; /**< documentation string for the autogenerated reference manual */ +}; + +/** Enumeration of all types of operators that HILTI supports. */ +enum class Kind { + Add, + Begin, + BitAnd, + BitOr, + BitXor, + Call, + Cast, + DecrPostfix, + DecrPrefix, + Delete, + Deref, + Difference, + DifferenceAssign, + Division, + DivisionAssign, + Equal, + End, + Greater, + GreaterEqual, + HasMember, + In, + IncrPostfix, + IncrPrefix, + Index, + Lower, + LowerEqual, + Member, + MemberCall, + Modulo, + Multiple, + MultipleAssign, + Negate, + New, + Power, + ShiftLeft, + ShiftRight, + SignNeg, + SignPos, + Size, + Sum, + SumAssign, + TryMember, + Unequal, + Unknown, + Unpack +}; + +/** Returns true for operator types that HILTI considers commutative. */ +constexpr auto is_commutative(Kind k) { + switch ( k ) { + case Kind::BitAnd: + case Kind::BitOr: + case Kind::BitXor: + case Kind::Equal: + case Kind::Unequal: + case Kind::Multiple: + case Kind::Sum: return true; + + case Kind::Add: + case Kind::Begin: + case Kind::Call: + case Kind::Cast: + case Kind::DecrPostfix: + case Kind::DecrPrefix: + case Kind::Delete: + case Kind::Deref: + case Kind::Difference: + case Kind::DifferenceAssign: + case Kind::Division: + case Kind::DivisionAssign: + case Kind::End: + case Kind::Greater: + case Kind::GreaterEqual: + case Kind::HasMember: + case Kind::In: + case Kind::IncrPostfix: + case Kind::IncrPrefix: + case Kind::Index: + case Kind::Lower: + case Kind::LowerEqual: + case Kind::Member: + case Kind::MemberCall: + case Kind::Modulo: + case Kind::MultipleAssign: + case Kind::Negate: + case Kind::New: + case Kind::Power: + case Kind::ShiftLeft: + case Kind::ShiftRight: + case Kind::SignNeg: + case Kind::SignPos: + case Kind::Size: + case Kind::SumAssign: + case Kind::TryMember: + case Kind::Unknown: + case Kind::Unpack: return false; + default: util::cannot_be_reached(); + }; +} + +namespace detail { +constexpr util::enum_::Value kinds[] = {{Kind::Add, "add"}, + {Kind::Begin, "begin"}, + {Kind::BitAnd, "&"}, + {Kind::BitOr, "|"}, + {Kind::BitXor, "^"}, + {Kind::Call, "call"}, + {Kind::Cast, "cast"}, + {Kind::DecrPostfix, "--"}, + {Kind::DecrPrefix, "--"}, + {Kind::Delete, "delete"}, + {Kind::Deref, "*"}, + {Kind::Division, "/"}, + {Kind::DivisionAssign, "/="}, + {Kind::Equal, "=="}, + {Kind::End, "end"}, + {Kind::Greater, ">"}, + {Kind::GreaterEqual, ">="}, + {Kind::HasMember, "?."}, + {Kind::In, "in"}, + {Kind::IncrPostfix, "++"}, + {Kind::IncrPrefix, "++"}, + {Kind::Index, "index"}, + {Kind::Lower, "<"}, + {Kind::LowerEqual, "<="}, + {Kind::Member, "."}, + {Kind::MemberCall, "method call"}, + {Kind::Negate, "~"}, + {Kind::New, "new"}, + {Kind::Difference, "-"}, + {Kind::DifferenceAssign, "-="}, + {Kind::Modulo, "%"}, + {Kind::Multiple, "*"}, + {Kind::MultipleAssign, "*="}, + {Kind::Sum, "+"}, + {Kind::SumAssign, "+="}, + {Kind::Power, "**"}, + {Kind::ShiftLeft, "<<"}, + {Kind::ShiftRight, ">>"}, + {Kind::SignNeg, "-"}, + {Kind::SignPos, "+"}, + {Kind::Size, "size"}, + {Kind::TryMember, ".?"}, + {Kind::Unequal, "!="}, + {Kind::Unknown, ""}, + {Kind::Unpack, "unpack"}}; +} // namespace detail + +/** + * Returns a descriptive string representation of an operator kind. This is + * meant just for display purposes, and does not correspond directly to the + * HILTI code representation (because thay may differs based on context). + */ +constexpr auto to_string(Kind m) { return util::enum_::to_string(m, detail::kinds); } + +namespace detail { +class Operator; + +#include + +} // namespace detail +} // namespace operator_ + +using Operator = operator_::detail::Operator; + +inline bool operator==(const Operator& x, const Operator& y) { + if ( &x == &y ) + return true; + + return x.typename_() == y.typename_(); +} + +} // namespace hilti diff --git a/hilti/include/ast/operators/address.h b/hilti/include/ast/operators/address.h new file mode 100644 index 000000000..faea3e7b5 --- /dev/null +++ b/hilti/include/ast/operators/address.h @@ -0,0 +1,29 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(address, Equal, type::Bool(), type::Address(), type::Address(), "Compares two address values.") +STANDARD_OPERATOR_2(address, Unequal, type::Bool(), type::Address(), type::Address(), "Compares two address values.") + +BEGIN_METHOD(address, Family) + auto signature() const { + return Signature{.self = type::Address(), + .result = builder::typeByID("hilti::AddressFamily"), + .id = "family", + .args = {}, + .doc = R"( +Returns the protocol family of the address, which can be IPv4 or IPv6. +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/all.h b/hilti/include/ast/operators/all.h new file mode 100644 index 000000000..ada6d2dfa --- /dev/null +++ b/hilti/include/ast/operators/all.h @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/operators/bool.h b/hilti/include/ast/operators/bool.h new file mode 100644 index 000000000..023cdd1bf --- /dev/null +++ b/hilti/include/ast/operators/bool.h @@ -0,0 +1,16 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(bool_, Equal, type::Bool(), type::Bool(), type::Bool(), "Compares two boolean values.") +STANDARD_OPERATOR_2(bool_, Unequal, type::Bool(), type::Bool(), type::Bool(), "Compares two boolean values.") + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/bytes.h b/hilti/include/ast/operators/bytes.h new file mode 100644 index 000000000..63ef877f1 --- /dev/null +++ b/hilti/include/ast/operators/bytes.h @@ -0,0 +1,373 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +// bytes::Iterator + +STANDARD_OPERATOR_1(bytes::iterator, Deref, type::UnsignedInteger(64), type::constant(type::bytes::Iterator()), + "Returns the byte the iterator is pointing to."); +STANDARD_OPERATOR_1(bytes::iterator, IncrPostfix, type::bytes::Iterator(), type::bytes::Iterator(), + "Advances the iterator by one byte, returning the previous position."); +STANDARD_OPERATOR_1(bytes::iterator, IncrPrefix, type::bytes::Iterator(), type::bytes::Iterator(), + "Advances the iterator by one byte, returning the new position."); + +STANDARD_OPERATOR_2( + bytes::iterator, Equal, type::Bool(), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same bytes value."); +STANDARD_OPERATOR_2( + bytes::iterator, Unequal, type::Bool(), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same bytes value."); +STANDARD_OPERATOR_2( + bytes::iterator, Lower, type::Bool(), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same bytes value."); +STANDARD_OPERATOR_2( + bytes::iterator, LowerEqual, type::Bool(), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same bytes value."); +STANDARD_OPERATOR_2( + bytes::iterator, Greater, type::Bool(), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same bytes value."); +STANDARD_OPERATOR_2( + bytes::iterator, GreaterEqual, type::Bool(), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same bytes value."); +STANDARD_OPERATOR_2( + bytes::iterator, Difference, type::SignedInteger(64), type::constant(type::bytes::Iterator()), + type::constant(type::bytes::Iterator()), + "Returns the number of bytes between the two iterators. The result will be negative if the second iterator points " + "to a location before the first. The result is undefined if the iterators do not refer to the same bytes instace."); +STANDARD_OPERATOR_2(bytes::iterator, Sum, type::bytes::Iterator(), type::constant(type::bytes::Iterator()), + type::UnsignedInteger(64), "Advances the iterator by the given number of bytes.") +STANDARD_OPERATOR_2(bytes::iterator, SumAssign, type::bytes::Iterator(), type::bytes::Iterator(), + type::UnsignedInteger(64), "Advances the iterator by the given number of bytes.") + +// Bytes + +STANDARD_OPERATOR_1(bytes, Size, type::UnsignedInteger(64), type::constant(type::Bytes()), + "Returns the number of bytes the value contains."); +STANDARD_OPERATOR_2(bytes, Equal, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Compares two bytes values lexicographically."); +STANDARD_OPERATOR_2(bytes, Unequal, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Compares two bytes values lexicographically."); +STANDARD_OPERATOR_2(bytes, Greater, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Compares two bytes values lexicographically."); +STANDARD_OPERATOR_2(bytes, GreaterEqual, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Compares two bytes values lexicographically."); +STANDARD_OPERATOR_2(bytes, In, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Returns true if the right-hand-side value contains the left-hand-side value as a subsequence."); +STANDARD_OPERATOR_2(bytes, Lower, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Compares two bytes values lexicographically."); +STANDARD_OPERATOR_2(bytes, LowerEqual, type::Bool(), type::constant(type::Bytes()), type::constant(type::Bytes()), + "Compares two bytes values lexicographically."); +STANDARD_OPERATOR_2(bytes, Sum, type::constant(type::Bytes()), type::constant(type::Bytes()), + type::constant(type::Bytes()), "Returns the concatentation of two bytes values."); +STANDARD_OPERATOR_2x(bytes, SumAssignBytes, SumAssign, type::Bytes(), type::Bytes(), type::constant(type::Bytes()), + "Appends one bytes value to another."); +STANDARD_OPERATOR_2x(bytes, SumAssignStreamView, SumAssign, type::Bytes(), type::Bytes(), + type::constant(type::stream::View()), "Appends a view of stream data to a bytes instance."); + +BEGIN_METHOD(bytes, Find) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Tuple({type::Bool(), type::bytes::Iterator()}), + .id = "find", + .args = {{.id = "needle", .type = type::constant(type::Bytes())}}, + .doc = R"( +Searches *needle* in the value's content. Returns a tuple of a boolean and an +iterator. If *needle* was found, the boolean will be true and the iterator will +point to its first occurance. If *needle* was not found, the boolean will be +false and the iterator will point to the last position so that everything before +it is guaranteed to not contain even a partial match of *needle*. Note that for a +simple yes/no result, you should use the ``in`` operator instead of this method, +as it's more efficient. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, LowerCase) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "lower", + .args = {{.id = "charset", + .type = type::Enum(type::Wildcard()), + .default_ = builder::id("hilti::Charset::UTF8")}}, + .doc = R"( +Returns a lower-case version of the bytes value, assuming its encoded in character set *charset*. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, UpperCase) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "upper", + .args = {{.id = "charset", + .type = type::Enum(type::Wildcard()), + .default_ = builder::id("hilti::Charset::UTF8")}}, + .doc = R"( +Returns an upper-case version of the bytes value, assuming its encoded in character set *charset*. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, At) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::bytes::Iterator(), + .id = "at", + .args = {{.id = "i", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Returns an iterator representing the offset *i* inside the bytes value. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, Split) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Vector(type::Bytes()), + .id = "split", + .args = {{.id = "sep", .type = type::constant(type::Bytes()), .optional = true}}, + .doc = R"( +Splits the bytes value at each occurence of *sep* and returns a vector +containing the individual pieces, with all separators removed. If the separator +is not found, the returned vector will have the whole bytes value as its single +element. If the separator is not given, or empty, the split will take place at +sequences of white spaces. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, Split1) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Tuple({type::Bytes(), type::Bytes()}), + .id = "split1", + .args = {{.id = "sep", .type = type::constant(type::Bytes()), .optional = true}}, + .doc = R"( +Splits the bytes value at the first occurence of *sep* and returns the two parts +as a 2-tuple, with the separator removed. If the separator is not found, the +returned tuple will have the whole bytes value as its first element and an empty value +as its second element. If the separator is not given, or empty, the split will +take place at the first sequence of white spaces. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, StartsWith) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bool(), + .id = "starts_with", + .args = {{.id = "b", .type = type::constant(type::Bytes())}}, + .doc = R"( +Returns true if the bytes value starts with *b*. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, Strip) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "strip", + .args = {{.id = "side", + .type = type::constant(type::Library("hilti::rt::bytes::Side")), + .optional = true}, + {.id = "set", .type = type::constant(type::Bytes()), .optional = true}}, + .doc = R"( +Removes leading and/or trailing sequences of all characters in *set* from the bytes +value. If *set* is not given, removes all white spaces. If *side* is given, +it indicates which side of the value should be stripped; ``Side::Both`` is the +default if not given. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, SubIterators) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "sub", + .args = {{.id = "begin", .type = type::bytes::Iterator()}, + {.id = "end", .type = type::bytes::Iterator()}}, + .doc = R"( +Returns the subsequence from *begin* to (but not including) *end*. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, SubIterator) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "sub", + .args = {{.id = "end", .type = type::bytes::Iterator()}}, + .doc = R"( +Returns the subsequence from the value's beginning to (but not including) *end*. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, SubOffsets) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "sub", + .args = {{.id = "begin", .type = type::UnsignedInteger(64)}, + {.id = "end", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Returns the subsequence from offset *begin* to (but not including) offset *end*. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, Join) + auto signature() const { + return Signature{ + .self = type::constant(type::Bytes()), + .result = type::Bytes(), + .id = "join", + .args = {{.id = "parts", .type = type::Vector(type::Wildcard())}}, + .doc = + R"(Returns the concatenation of all elements in the *parts* list rendered as printable-strings and separated by the bytes value providing this method.)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, ToIntAscii) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::SignedInteger(64), + .id = "to_int", + .args = {{.id = "base", .type = type::UnsignedInteger(64), .optional = true}}, + .doc = + R"( +Interprets the data as representing an ASCII-encoded number and converts +that into a signed integer, using a base of *base*. If *base* is not given, the +default is 10. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, ToUIntAscii) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::UnsignedInteger(64), + .id = "to_uint", + .args = {{.id = "base", .type = type::UnsignedInteger(64), .optional = true}}, + .doc = + R"( +Interprets the data as representing an ASCII-encoded number and converts +that into an unsigned integer, using a base of *base*. If *base* is not given, the +default is 10. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, ToIntBinary) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::SignedInteger(64), + .id = "to_int", + .args = {{.id = "byte_order", .type = type::Enum(type::Wildcard())}}, + .doc = + R"( +Interprets the ``bytes`` as representing an binary number encoded with the given +byte order, and converts it into signed integer. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, ToUIntBinary) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::UnsignedInteger(64), + .id = "to_uint", + .args = {{.id = "byte_order", .type = type::Enum(type::Wildcard())}}, + .doc = + R"( +Interprets the ``bytes`` as representing an binary number encoded with the given +byte order, and converts it into an unsigned integer. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, ToTimeAscii) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Time(), + .id = "to_time", + .args = {{.id = "base", .type = type::UnsignedInteger(64), .optional = true}}, + .doc = + R"( +Interprets the ``bytes`` as representing a number of seconds since the epoch in +the form of an ASCII-encoded number, and converts it into a time value using a +base of *base*. If *base* is not given, the default is 10. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, ToTimeBinary) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Time(), + .id = "to_time", + .args = {{.id = "byte_order", .type = type::Enum(type::Wildcard())}}, + .doc = + R"( +Interprets the ``bytes`` as representing as number of seconds since the epoch in +the form of an binary number encoded with the given byte order, and converts it +into a time value. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, Decode) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::String(), + .id = "decode", + .args = {{.id = "charset", .type = type::Enum(type::Wildcard())}}, + .doc = + R"( +Interprets the ``bytes`` as representing an binary string encoded with the given +character set, and converts it into a UTF8 string. +)"}; + } +END_METHOD + +BEGIN_METHOD(bytes, Match) + auto signature() const { + return Signature{.self = type::constant(type::Bytes()), + .result = type::Result(type::Bytes()), + .id = "match", + .args = {{.id = "regex", .type = type::RegExp()}, + {.id = "group", .type = type::UnsignedInteger(64), .optional = true}}, + .doc = + R"( +Matches the ``bytes`` object against the regular expression *regex*. Returns the matching +part or, if *group* is given the corresponding subgroup. +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/common.h b/hilti/include/ast/operators/common.h new file mode 100644 index 000000000..178ab2a5c --- /dev/null +++ b/hilti/include/ast/operators/common.h @@ -0,0 +1,263 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +/** Internal helper macro. */ +#define __BEGIN_OPERATOR_CUSTOM(ns, op, cls) \ + namespace ns { \ + /** AST node for a the operator expression. */ \ + class cls : public hilti::expression::ResolvedOperatorBase { \ + public: \ + using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; \ + \ + /** Class implementing operator interface. */ \ + struct Operator : public hilti::trait::isOperator { \ + static ::hilti::operator_::Kind kind() { return ::hilti::operator_::Kind::op; } \ + \ + hilti::Expression instantiate(const std::vector& operands, const Meta& meta) const; \ + std::string docNamespace() const { return #ns; } + +/** Internal helper macro. */ +#define __END_OPERATOR_CUSTOM \ + } \ + ; \ + \ +private: \ + } \ + ; \ + } + +/** + * Starts definition of an operator. This macro is for the simple case where + * the return type is static and no custom validation is needed. + * + * @param ns namespace to define the operator in + * @param op ``operator_::Kind`` for the operator + */ +#define BEGIN_OPERATOR(ns, op) __BEGIN_OPERATOR_CUSTOM(ns, op, op) + +/** Ends definition of a method call operator. */ +#define END_OPERATOR \ + std::vector operands() const { return signature().args; } \ + \ + std::string doc() const { return signature().doc; } \ + \ + hilti::Type result(const std::vector& ops) const { \ + return *hilti::operator_::type(signature().result, ops, ops); \ + } \ + \ + bool isLhs() const { return signature().lhs; } \ + \ + void validate(const hilti::expression::ResolvedOperator& /* i */, hilti::operator_::const_position_t /* p */) \ + const {} \ + \ + __END_OPERATOR_CUSTOM + +/** + * Starts definition of an operator that provides its own implementation of the + * API's methods. + * + * @param ns namespace to define the operator in + * @param op ``operator_::dnd`` for the operator + */ +#define BEGIN_OPERATOR_CUSTOM(ns, op) __BEGIN_OPERATOR_CUSTOM(ns, op, op) + +/** + * Ends definition of an operator that provides its own implementation of the + * API's methods.. + */ +#define END_OPERATOR_CUSTOM __END_OPERATOR_CUSTOM + +/** + * Starts definition of an operator that provides its own implementation of the + * API's methods. This version allows to specify a custom class name, which + * allows for overloading. + * + * @param ns namespace to define the operator in + * @param cls name of the operator's class + * @param op ``operator_::Kind`` for the operator + */ +#define BEGIN_OPERATOR_CUSTOM_x(ns, cls, op) __BEGIN_OPERATOR_CUSTOM(ns, op, cls) + +/** + * Ends definition of an operator that provides its own implementation of the + * API's methods. This ends the version that specify's a custom class name. + */ +#define END_OPERATOR_CUSTOM_x __END_OPERATOR_CUSTOM + + +/** + * Shortcut version for defining a straight-forward operator with 1 operand. + */ +#define STANDARD_OPERATOR_1(ns, op, result_, ty_op1, doc_) \ + BEGIN_OPERATOR(ns, op) \ + auto signature() const { \ + return hilti::operator_::Signature{.result = result_, \ + .args = \ + { \ + {.id = "op", .type = ty_op1}, \ + }, \ + .doc = doc_}; \ + } \ + END_OPERATOR + +/** + * Shortcut version for defining a straight-forward operator with 1 operand. + */ +#define STANDARD_OPERATOR_1x(ns, cls, op, result_, ty_op1, doc_) \ + __BEGIN_OPERATOR_CUSTOM(ns, op, cls) \ + auto signature() const { \ + return hilti::operator_::Signature{.result = result_, \ + .args = \ + { \ + {.id = "op", .type = ty_op1}, \ + }, \ + .doc = doc_}; \ + } \ + END_OPERATOR + +/** + * Shortcut version for defining a straight-forward operator with 2 operands. + */ +#define STANDARD_OPERATOR_2(ns, op, result_, ty_op1, ty_op2, doc_) \ + BEGIN_OPERATOR(ns, op) \ + auto signature() const { \ + return hilti::operator_::Signature{.result = result_, \ + .args = {{.id = "op0", .type = ty_op1}, {.id = "op1", .type = ty_op2}}, \ + .doc = doc_}; \ + } \ + END_OPERATOR + +/** + * Shortcut version for defining a straight-forward operator with 2 operands. + */ +#define STANDARD_OPERATOR_2x(ns, cls, op, result_, ty_op1, ty_op2, doc_) \ + __BEGIN_OPERATOR_CUSTOM(ns, op, cls) \ + auto signature() const { \ + return hilti::operator_::Signature{.result = result_, \ + .args = {{.id = "op0", .type = ty_op1}, {.id = "op1", .type = ty_op2}}, \ + .doc = doc_}; \ + } \ + END_OPERATOR + +/** + * Shortcut version for defining a straight-forward LHS operator with 2 operands. + */ +#define STANDARD_OPERATOR_2x_lhs(ns, cls, op, result_, ty_op1, ty_op2, doc_) \ + __BEGIN_OPERATOR_CUSTOM(ns, op, cls) \ + auto signature() const { \ + return hilti::operator_::Signature{.lhs = true, \ + .result = result_, \ + .args = {{.id = "op0", .type = ty_op1}, {.id = "op1", .type = ty_op2}}, \ + .doc = doc_}; \ + } \ + END_OPERATOR + +/** + * Shortcut version for defining a straight-forward operator with 3 operands. + */ +#define STANDARD_OPERATOR_3(ns, op, result_, ty_op1, ty_op2, ty_op3, doc_) \ + BEGIN_OPERATOR(ns, op) \ + auto signature() const { \ + return hilti::operator_::Signature{.result = result_, \ + .args = {{.id = "op0", .type = ty_op1}, \ + {.id = "op1", .type = ty_op2}, \ + {.id = "op2", .type = ty_op3}}, \ + .doc = doc_}; \ + } \ + END_OPERATOR + +/** + * Starts definition of a method call operator. This macroi is for the simple + * case where the return type is static and no custom validation is needed. + * + * @param ns namespace to define the operator in + * @param op Name for the operator (i.e., it's C++-level ID) + */ +#define BEGIN_METHOD(ns, method) __BEGIN_OPERATOR_CUSTOM(ns, MemberCall, method) + +/** + * Starts definition of a method call operator that provides its own result() + * and validate() implementation. + * + * @param ns namespace to define the operator in + * @param op Name for the operator (i.e., it's C++-level ID) + */ +#define BEGIN_METHOD_CUSTOM_RESULT(ns, method) __BEGIN_OPERATOR_CUSTOM(ns, MemberCall, method) + +/** Internal helper macro. */ +#define __END_METHOD \ + std::vector operands() const { \ + return {{.type = signature().self}, \ + {.type = hilti::type::Member(signature().id)}, \ + {.type = hilti::type::OperandList(signature().args)}}; \ + } \ + \ + std::string doc() const { return signature().doc; } + +/** Ends definition of a method call operator. */ +#define END_METHOD \ + __END_METHOD \ + \ + hilti::Type result(const std::vector& ops) const { \ + return *hilti::operator_::type(signature().result, ops, ops); \ + } \ + \ + bool isLhs() const { return false; } \ + \ + void validate(const hilti::expression::ResolvedOperator& /* i */, hilti::operator_::const_position_t /* p */) \ + const {} \ + \ + __END_OPERATOR_CUSTOM + +/** + * Ends definition of a method call operator that provides its own result() + * and validate() implementation. + */ +#define END_METHOD_CUSTOM_RESULT \ + __END_METHOD \ + __END_OPERATOR_CUSTOM + +/** + * Starts definition of a constructor-style call operator. + * + * @param ns namespace to define the operator in + * @param cls name of the operator's class + */ +#define BEGIN_CTOR(ns, cls) __BEGIN_OPERATOR_CUSTOM(ns, Call, cls) + +#define END_CTOR \ + std::vector operands() const { \ + return {{.type = hilti::type::Type_(ctorType())}, {.type = hilti::type::OperandList(signature().args)}}; \ + } \ + \ + std::string doc() const { return signature().doc; } \ + \ + hilti::Type result(const std::vector& ops) const { \ + if ( ops.size() ) \ + return ops[0].type().as().typeValue(); \ + \ + return ctorType(); \ + } \ + \ + bool isLhs() const { return false; } \ + \ + void validate(const hilti::expression::ResolvedOperator& /* i */, hilti::operator_::const_position_t /* p */) \ + const {} \ + \ + __END_OPERATOR_CUSTOM + +/** + * No-op to have the auto-generated code pick up on an operator that's + * fully defined separately. + */ +#define OPERATOR_DECLARE_ONLY(ns, cls) diff --git a/hilti/include/ast/operators/enum.h b/hilti/include/ast/operators/enum.h new file mode 100644 index 000000000..de4e232b5 --- /dev/null +++ b/hilti/include/ast/operators/enum.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti::operator_ { + +STANDARD_OPERATOR_2(enum_, Equal, type::Bool(), type::constant(type::Enum(type::Wildcard())), + operator_::sameTypeAs(0, "enum<*>"), "Compares two enum values."); +STANDARD_OPERATOR_2(enum_, Unequal, type::Bool(), type::constant(type::Enum(type::Wildcard())), + operator_::sameTypeAs(0, "enum<*>"), "Compares two enum values."); +STANDARD_OPERATOR_2x(enum_, CastFromUnsignedInteger, Cast, operator_::typedType(1, "enum<*>"), + type::UnsignedInteger(type::Wildcard()), type::Type_(type::Enum(type::Wildcard())), + "Casts an unsigned integer value to an enum instance. The value does *not* need to correspond to " + "any of the type's enumerator labels"); +STANDARD_OPERATOR_2x( + enum_, CastToSignedInteger, Cast, operator_::typedType(1, "int"), type::Enum(type::Wildcard()), + type::Type_(type::SignedInteger(type::Wildcard())), + "Casts an enum value into a signed integer. If the enum value is ``Undef``, this will return ``-1``."); +STANDARD_OPERATOR_2x( + enum_, CastToUnsignedInteger, Cast, operator_::typedType(1, "uint"), type::Enum(type::Wildcard()), + type::Type_(type::UnsignedInteger(type::Wildcard())), + "Casts an enum value into a unsigned integer. This will throw an exception if the enum value is ``Undef``."); + +BEGIN_CTOR(enum_, Ctor) + auto ctorType() const { return type::Enum(type::Wildcard()); } + + auto signature() const { + return Signature{.args = {{.id = "value", .type = type::UnsignedInteger(type::Wildcard())}}, .doc = R"( +Instantiates an enum instance intialized from an integer value. The value does *not* need to correspond to any of the type's enumerator labels. +)"}; + } +END_CTOR + +} // namespace hilti::operator_ diff --git a/hilti/include/ast/operators/error.h b/hilti/include/ast/operators/error.h new file mode 100644 index 000000000..f9b1d25b2 --- /dev/null +++ b/hilti/include/ast/operators/error.h @@ -0,0 +1,22 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace operator_ { + +BEGIN_METHOD(error, Description) + auto signature() const { + return Signature{.self = type::Error(), + .result = type::String(), + .id = "description", + .args = {}, + .doc = "Retrieves the textual description associated with the error."}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/exception.h b/hilti/include/ast/operators/exception.h new file mode 100644 index 000000000..f6eb1240f --- /dev/null +++ b/hilti/include/ast/operators/exception.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace operator_ { + +BEGIN_CTOR(exception, Ctor) + auto ctorType() const { return type::Exception(type::Wildcard()); } + + auto signature() const { + return Signature{.args = {{.id = "msg", .type = type::String()}}, .doc = R"( +Instantiates an instance of the exception type carrying the error message *msg*. +)"}; + } +END_CTOR + +BEGIN_METHOD(exception, Description) + auto signature() const { + return Signature{.self = type::Exception(type::Wildcard()), + .result = type::String(), + .id = "description", + .args = {}, + .doc = R"( +Returns the textual message associated with an exception object. +)"}; + } +END_METHOD +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/function.h b/hilti/include/ast/operators/function.h new file mode 100644 index 000000000..35d5477d9 --- /dev/null +++ b/hilti/include/ast/operators/function.h @@ -0,0 +1,57 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace operator_ { + +OPERATOR_DECLARE_ONLY(function, Call) + +namespace function { + +class Call : public hilti::expression::ResolvedOperatorBase { +public: + using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + + struct Operator : public hilti::trait::isOperator { + Operator(const Scope::Referee& r, const type::Function& ftype) { + auto op0 = operator_::Operand{.type = type::Any()}; // IDs won't be resolved + auto op1 = operator_::Operand{.type = ftype.operands()}; + _referee = r; + _operands = {op0, op1}; + _result = ftype.result().type(); + } + + static operator_::Kind kind() { return operator_::Kind::Call; } + std::vector operands() const { return _operands; } + Type result(const std::vector& /* ops */) const { return _result; } + bool isLhs() const { return false; } + void validate(const expression::ResolvedOperator& /* i */, operator_::const_position_t /* p */) const {} + std::string doc() const { return ""; } + std::string docNamespace() const { return ""; } + + Expression instantiate(const std::vector& operands, const Meta& meta) const { + auto ops = std::vector{expression::ResolvedID(ID(_referee.qualified), NodeRef(_referee.node), + _referee.node->meta()), + operands[1]}; + + auto ro = expression::ResolvedOperator(Call(*this, ops, meta)); + ro.setMeta(meta); + return std::move(ro); + } + + private: + Scope::Referee _referee; + std::vector _operands; + Type _result; + }; +}; + +} // namespace function + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/generic.h b/hilti/include/ast/operators/generic.h new file mode 100644 index 000000000..45efb4b4d --- /dev/null +++ b/hilti/include/ast/operators/generic.h @@ -0,0 +1,165 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +BEGIN_OPERATOR_CUSTOM(generic, Unpack) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + auto data_type = ops[1].type().as().types()[0]; + return type::Result(type::Tuple({ops[0].type().as().typeValue(), data_type}, ops[0].meta())); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Type_(type::Wildcard())}, {.type = type::Tuple(type::Wildcard())}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /*p */) const { + auto data_type = i.op1().type().template as().types()[0]; + + if ( ! (data_type.isA() || data_type.isA()) ) + logger().error("unpack() can be used only with bytes or a stream view as input", i); + } + + std::string doc() const { return "Unpacks a value from a binary representation."; } +END_OPERATOR_CUSTOM + +BEGIN_OPERATOR_CUSTOM(generic, Begin) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return type::isIterable(ops[0].type()) ? ops[0].type().iteratorType(ops[0].isConstant()) : type::unknown; + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return { + {.type = type::Any()}, + }; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + if ( ! type::isIterable(i.operands()[0].type()) ) + logger().error("not an iterable type", i); + } + + std::string doc() const { return "Returns an iterator to the beginning of a container's content."; } +END_OPERATOR_CUSTOM + +BEGIN_OPERATOR_CUSTOM(generic, End) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return type::isIterable(ops[0].type()) ? ops[0].type().iteratorType(ops[0].isConstant()) : type::unknown; + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return { + {.type = type::Any()}, + }; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + if ( ! type::isIterable(i.operands()[0].type()) ) + logger().error("not an iterable type", i); + } + + std::string doc() const { return "Returns an iterator to the end of a container's content."; } +END_OPERATOR_CUSTOM + +BEGIN_OPERATOR_CUSTOM(generic, New) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly("strong_ref"); + + auto t = ops[0].type(); + + if ( auto tv = ops[0].type().tryAs() ) + t = tv->typeValue(); + + return type::StrongReference(t, t.meta()); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return { + {.id = "t", .type = type::Any()}, + {.type = type::Tuple(type::Wildcard())}, + }; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + auto t = i.operands()[0].type(); + + if ( auto tv = i.operands()[0].type().tryAs() ) + t = tv->typeValue(); + + if ( ! type::isAllocable(t) ) + logger().error("not an allocable type", i); + } + + std::string doc() const { + return R"( +Returns a reference to an instance of a type newly allocated on the heap. +If `x' is a type, a default instance of that type will be allocated. +If `x` is an expression, an instance of the expression's type will be allocated and initialized with the value of the expression. +)"; + } +END_OPERATOR_CUSTOM + +/** + * Operator created internally by the resolver for a cast expression + * requesting a type coercion. This is mainly just a wrapper around a + * CoercedExpression so that we don't loose the information that it was cast. + */ +OPERATOR_DECLARE_ONLY(generic, CastedCoercion) + +namespace generic { + +class CastedCoercion : public hilti::expression::ResolvedOperatorBase { +public: + using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + + struct Operator : public hilti::trait::isOperator { + Operator() = default; + + static operator_::Kind kind() { return operator_::Kind::Cast; } + std::vector operands() const { return {}; } // Won't participate in overload resolution + Type result(const std::vector& ops) const { return ops[1].as().typeValue(); } + bool isLhs() const { return false; } + void validate(const expression::ResolvedOperator& /* i */, operator_::const_position_t /* p */) const {} + std::string doc() const { return ""; } + std::string docNamespace() const { return ""; } + + Expression instantiate(const std::vector& operands, const Meta& meta) const { + auto ro = expression::ResolvedOperator(CastedCoercion(*this, operands, meta)); + ro.setMeta(meta); + return std::move(ro); + } + }; +}; +} // namespace generic + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/interval.h b/hilti/include/ast/operators/interval.h new file mode 100644 index 000000000..e4e34b8c8 --- /dev/null +++ b/hilti/include/ast/operators/interval.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(interval, Equal, type::Bool(), type::Interval(), type::Interval(), "Compares two interval values.") +STANDARD_OPERATOR_2(interval, Unequal, type::Bool(), type::Interval(), type::Interval(), + "Compares two interval values.") +STANDARD_OPERATOR_2(interval, Sum, type::Interval(), type::Interval(), type::Interval(), + "Returns the sum of the intervals."); +STANDARD_OPERATOR_2(interval, Difference, type::Interval(), type::Interval(), type::Interval(), + "Returns the difference of the intervals."); +STANDARD_OPERATOR_2(interval, Greater, type::Bool(), type::Interval(), type::Interval(), "Compares the intervals."); +STANDARD_OPERATOR_2(interval, GreaterEqual, type::Bool(), type::Interval(), type::Interval(), + "Compares the intervals."); +STANDARD_OPERATOR_2(interval, Lower, type::Bool(), type::Interval(), type::Interval(), "Compares the intervals."); +STANDARD_OPERATOR_2(interval, LowerEqual, type::Bool(), type::Interval(), type::Interval(), "Compares the intervals."); +STANDARD_OPERATOR_2x(interval, MultipleUnsignedInteger, Multiple, type::Interval(), type::Interval(), + type::UnsignedInteger(64), "Multiples the interval with the given factor."); +STANDARD_OPERATOR_2x(interval, MultipleReal, Multiple, type::Interval(), type::Interval(), type::Real(), + "Multiples the interval with the given factor."); +STANDARD_OPERATOR_2x(interval, CastFromReal, Cast, type::Interval(), type::Real(), type::Type_(type::Interval()), + "Interprets the real value as number of seconds."); +STANDARD_OPERATOR_2x(interval, CastFromSignedInteger, Cast, type::Interval(), type::SignedInteger(type::Wildcard()), + type::Type_(type::Interval()), "Interprets the signed integer value as number of seconds."); +STANDARD_OPERATOR_2x(interval, CastFromUnsignedInteger, Cast, type::Interval(), type::UnsignedInteger(type::Wildcard()), + type::Type_(type::Interval()), "Interprets the unsigned integer value as number of seconds."); + +BEGIN_METHOD(interval, Seconds) + auto signature() const { + return Signature{.self = type::Interval(), .result = type::Real(), .id = "seconds", .args = {}, .doc = R"( +Returns the interval as a real value representing seconds. +)"}; + } +END_METHOD + +BEGIN_METHOD(interval, Nanoseconds) + auto signature() const { + return Signature{.self = type::Interval(), .result = type::Real(), .id = "nanoseconds", .args = {}, .doc = R"( +Returns the interval as an integer value representing nanoseconds. +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/list.h b/hilti/include/ast/operators/list.h new file mode 100644 index 000000000..d7b7571c4 --- /dev/null +++ b/hilti/include/ast/operators/list.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_1(list::iterator, Deref, operator_::dereferencedType(0), + type::constant(type::list::Iterator(type::Wildcard())), + "Returns the list element that the iterator refers to."); +STANDARD_OPERATOR_1(list::iterator, IncrPostfix, operator_::sameTypeAs(0, "iterator>"), + type::list::Iterator(type::Wildcard()), + "Advances the iterator by one list element, returning the previous position."); +STANDARD_OPERATOR_1(list::iterator, IncrPrefix, operator_::sameTypeAs(0, "iterator>"), + type::list::Iterator(type::Wildcard()), + "Advances the iterator by one list element, returning the new position."); +STANDARD_OPERATOR_2(list::iterator, Equal, type::Bool(), type::constant(type::list::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two lists iterators refer to the same location."); +STANDARD_OPERATOR_2(list::iterator, Unequal, type::Bool(), type::constant(type::list::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two lists iterators refer to different locations."); + +STANDARD_OPERATOR_1(list, Size, type::UnsignedInteger(64), type::constant(type::List(type::Wildcard())), + "Returns the number of elements a list contains."); +STANDARD_OPERATOR_2(list, Equal, type::Bool(), type::constant(type::List(type::Wildcard())), + operator_::sameTypeAs(0, "list<*>"), "Compares two lists element-wise."); +STANDARD_OPERATOR_2(list, Unequal, type::Bool(), type::constant(type::List(type::Wildcard())), + operator_::sameTypeAs(0, "list<*>"), "Compares two lists element-wise."); + +} // namespace operator_ + +} // namespace hilti diff --git a/hilti/include/ast/operators/map.h b/hilti/include/ast/operators/map.h new file mode 100644 index 000000000..a97b1b45c --- /dev/null +++ b/hilti/include/ast/operators/map.h @@ -0,0 +1,98 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +namespace detail { + +static inline auto constantKeyType(unsigned int op, const char* doc = "") { + return [=](const std::vector& /* orig_ops */, + const std::vector& resolved_ops) -> std::optional { + if ( resolved_ops.empty() ) + return type::DocOnly(doc); + + if ( op >= resolved_ops.size() ) { + logger().internalError( + util::fmt("keyType(): index %d out of range, only %" PRIu64 " ops available", op, resolved_ops.size())); + } + + return type::constant(resolved_ops[op].type().as().keyType()); + }; +} + +} // namespace detail + +STANDARD_OPERATOR_1(map::iterator, Deref, operator_::dereferencedType(0), + type::constant(type::map::Iterator(type::Wildcard())), + "Returns the map element that the iterator refers to."); +STANDARD_OPERATOR_1(map::iterator, IncrPostfix, operator_::sameTypeAs(0, "iterator>"), + type::map::Iterator(type::Wildcard()), + "Advances the iterator by one map element, returning the previous position."); +STANDARD_OPERATOR_1(map::iterator, IncrPrefix, operator_::sameTypeAs(0, "iterator>"), + type::map::Iterator(type::Wildcard()), + "Advances the iterator by one map element, returning the new position."); +STANDARD_OPERATOR_2(map::iterator, Equal, type::Bool(), type::constant(type::map::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two maps iterators refer to the same location."); +STANDARD_OPERATOR_2(map::iterator, Unequal, type::Bool(), type::constant(type::map::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two maps iterators refer to different locations."); + +STANDARD_OPERATOR_1(map, Size, type::UnsignedInteger(64), type::constant(type::Map(type::Wildcard())), + "Returns the number of elements a map contains."); +STANDARD_OPERATOR_2(map, Equal, type::Bool(), type::constant(type::Map(type::Wildcard())), + operator_::sameTypeAs(0, "map<*>"), "Compares two maps element-wise."); +STANDARD_OPERATOR_2(map, Unequal, type::Bool(), type::constant(type::Map(type::Wildcard())), + operator_::sameTypeAs(0, "map<*>"), "Compares two maps element-wise."); +STANDARD_OPERATOR_2(map, In, type::Bool(), type::Any(), type::constant(type::Map(type::Wildcard())), + "Returns true if an element is part of the map."); +STANDARD_OPERATOR_2(map, Delete, type::Void(), type::Map(type::Wildcard()), detail::constantKeyType(0), + "Removes an element from the map.") + +STANDARD_OPERATOR_2x(map, IndexConst, Index, operator_::constantElementType(0), + type::constant(type::Map(type::Wildcard())), type::Any(), + "Returns the map's element for the given key."); +STANDARD_OPERATOR_2x_lhs(map, IndexNonConst, Index, operator_::elementType(0), type::Map(type::Wildcard()), type::Any(), + "Returns the map's element for the given key. The key must exists, otherwise the operation " + "will throw a runtime error."); + + +BEGIN_METHOD(map, Get) + auto signature() const { + return Signature{.self = type::Map(type::Wildcard()), + .result = operator_::elementType(0), + .id = "get", + .args = {{.id = "key", .type = type::Any()}, + {.id = "default", .type = type::Any(), .optional = true}}, + .doc = R"( +Returns the map's element for the given key. If the key does not exist, returns +the default value if provided; otherwise throws a runtime error. +)"}; + } +END_METHOD + +BEGIN_METHOD(map, Clear) + auto signature() const { + return Signature{.self = type::Map(type::Wildcard()), + .result = type::Void(), + .id = "clear", + .args = {}, + .doc = R"( +Removes all elements from the map. +)"}; + } +END_METHOD + + +} // namespace operator_ + +} // namespace hilti diff --git a/hilti/include/ast/operators/network.h b/hilti/include/ast/operators/network.h new file mode 100644 index 000000000..e433e32a7 --- /dev/null +++ b/hilti/include/ast/operators/network.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(network, Equal, type::Bool(), type::Network(), type::Network(), "Compares two network values.") +STANDARD_OPERATOR_2(network, Unequal, type::Bool(), type::Network(), type::Network(), "Compares two network values.") +STANDARD_OPERATOR_2(network, In, type::Bool(), type::Address(), type::Network(), + "Returns true if the address is part of the network range.") + +BEGIN_METHOD(network, Family) + auto signature() const { + return Signature{.self = type::Network(), + .result = builder::typeByID("hilti::AddressFamily"), + .id = "family", + .args = {}, + .doc = R"( +Returns the protocol family of the network, which can be IPv4 or IPv6. +)"}; + } +END_METHOD + +BEGIN_METHOD(network, Prefix) + auto signature() const { + return Signature{.self = type::Network(), .result = type::Address(), .id = "prefix", .args = {}, .doc = R"( +Returns the network's prefix as a masked IP address. +)"}; + } +END_METHOD + +BEGIN_METHOD(network, Length) + auto signature() const { + return Signature{.self = type::Network(), + .result = type::SignedInteger(64), + .id = "length", + .args = {}, + .doc = R"( +Returns the length of the network's prefix. +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/optional.h b/hilti/include/ast/operators/optional.h new file mode 100644 index 000000000..45bf52328 --- /dev/null +++ b/hilti/include/ast/operators/optional.h @@ -0,0 +1,15 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_1(optional, Deref, operator_::dereferencedType(0), type::constant(type::Optional(type::Wildcard())), + "Returns the element stored, or throws an exception if none."); + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/port.h b/hilti/include/ast/operators/port.h new file mode 100644 index 000000000..3d939dc30 --- /dev/null +++ b/hilti/include/ast/operators/port.h @@ -0,0 +1,28 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(port, Equal, type::Bool(), type::Port(), type::Port(), "Compares two port values.") +STANDARD_OPERATOR_2(port, Unequal, type::Bool(), type::Port(), type::Port(), "Compares two port values.") + +BEGIN_METHOD(port, Protocol) + auto signature() const { + return Signature{.self = type::Port(), + .result = builder::typeByID("hilti::Protocol"), + .id = "protocol", + .args = {}, + .doc = R"( +Returns the protocol the port is using (such as UDP or TCP). +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/real.h b/hilti/include/ast/operators/real.h new file mode 100644 index 000000000..830bba616 --- /dev/null +++ b/hilti/include/ast/operators/real.h @@ -0,0 +1,48 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { +STANDARD_OPERATOR_1(real, SignNeg, type::Real(), type::Real(), "Inverts the sign of the real."); +STANDARD_OPERATOR_2(real, Difference, type::Real(), type::Real(), type::Real(), + "Returns the difference between the two values."); +STANDARD_OPERATOR_2(real, DifferenceAssign, type::Real(), type::Real(), type::Real(), + "Subtracts the second value from the first, assigning the new value."); +STANDARD_OPERATOR_2(real, Division, type::Real(), type::Real(), type::Real(), "Divides the first value by the second."); +STANDARD_OPERATOR_2(real, DivisionAssign, type::Real(), type::Real(), type::Real(), + "Divides the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(real, Equal, type::Bool(), type::Real(), type::Real(), "Compares the two reals."); +STANDARD_OPERATOR_2(real, Greater, type::Bool(), type::Real(), type::Real(), "Compares the two reals."); +STANDARD_OPERATOR_2(real, GreaterEqual, type::Bool(), type::Real(), type::Real(), "Compares the two reals."); +STANDARD_OPERATOR_2(real, Lower, type::Bool(), type::Real(), type::Real(), "Compares the two reals."); +STANDARD_OPERATOR_2(real, LowerEqual, type::Bool(), type::Real(), type::Real(), "Compares the two reals."); +STANDARD_OPERATOR_2(real, Modulo, type::Real(), type::Real(), type::Real(), + "Computes the modulus of the first real divided by the second."); +STANDARD_OPERATOR_2(real, Multiple, type::Real(), type::Real(), type::Real(), + "Multiplies the first real by the second."); +STANDARD_OPERATOR_2(real, MultipleAssign, type::Real(), type::Real(), type::Real(), + "Multiplies the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(real, Power, type::Real(), type::Real(), type::Real(), + "Computes the first real raised to the power of the second."); +STANDARD_OPERATOR_2(real, Sum, type::Real(), type::Real(), type::Real(), "Returns the sum of the reals."); +STANDARD_OPERATOR_2(real, SumAssign, type::Real(), type::Real(), type::Real(), + "Adds the first real to the second, assigning the new value."); +STANDARD_OPERATOR_2(real, Unequal, type::Bool(), type::Real(), type::Real(), "Compares the two reals."); + +STANDARD_OPERATOR_2x(real, CastUnsignedInteger, Cast, operator_::typedType(1, "uint<*>"), type::Real(), + type::Type_(type::UnsignedInteger(type::Wildcard())), + "Converts the value to an unsigned integer type, accepting any loss of information."); +STANDARD_OPERATOR_2x(real, CastSignedInteger, Cast, operator_::typedType(1, "int<*>"), type::Real(), + type::Type_(type::SignedInteger(type::Wildcard())), + "Converts the value to a signed integer type, accepting any loss of information."); + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/reference.h b/hilti/include/ast/operators/reference.h new file mode 100644 index 000000000..d10ce165d --- /dev/null +++ b/hilti/include/ast/operators/reference.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_1(strong_reference, Deref, operator_::dereferencedType(0), + type::constant(type::StrongReference(type::Wildcard())), + "Returns the referenced instance, or throws an exception if none or expired."); +STANDARD_OPERATOR_2(strong_reference, Equal, type::Bool(), type::constant(type::StrongReference(type::Wildcard())), + operator_::sameTypeAs(0), "Returns true if both operands reference the same instance.") +STANDARD_OPERATOR_2(strong_reference, Unequal, type::Bool(), type::constant(type::StrongReference(type::Wildcard())), + operator_::sameTypeAs(0), "Returns true if the two operands reference different instances.") + +STANDARD_OPERATOR_1(weak_reference, Deref, operator_::dereferencedType(0), + type::constant(type::WeakReference(type::Wildcard())), + "Returns the referenced instance, or throws an exception if none or expired."); +STANDARD_OPERATOR_2(weak_reference, Equal, type::Bool(), type::constant(type::WeakReference(type::Wildcard())), + operator_::sameTypeAs(0), "Returns true if both operands reference the same instance.") +STANDARD_OPERATOR_2(weak_reference, Unequal, type::Bool(), type::constant(type::WeakReference(type::Wildcard())), + operator_::sameTypeAs(0), "Returns true if the two operands reference different instances.") + +STANDARD_OPERATOR_1(value_reference, Deref, operator_::dereferencedType(0), + type::constant(type::ValueReference(type::Wildcard())), + "Returns the referenced instance, or throws an exception if none or expired."); +STANDARD_OPERATOR_2(value_reference, Equal, type::Bool(), type::constant(type::ValueReference(type::Wildcard())), + operator_::sameTypeAs(0), "Returns true if the values of both operands are equal.") +STANDARD_OPERATOR_2(value_reference, Unequal, type::Bool(), type::constant(type::ValueReference(type::Wildcard())), + operator_::sameTypeAs(0), "Returns true if the values of both operands are not equal.") + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/regexp.h b/hilti/include/ast/operators/regexp.h new file mode 100644 index 000000000..7ea8e374f --- /dev/null +++ b/hilti/include/ast/operators/regexp.h @@ -0,0 +1,120 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti::operator_ { + +BEGIN_METHOD(regexp, Find) + auto signature() const { + return Signature{.self = type::RegExp(), + .result = type::SignedInteger(32), + .id = "find", + .args = {{.id = "data", .type = type::constant(type::Bytes())}}, + .doc = R"( +Searches the regular expression in *data*. If found, returns an integer that's greater +than zero. If multiple patterns have been compiled for parallel matching, that +integer will be the ID of the matching pattern. Returns -1 if the regular +expression is not found, but could still match if more data were added to the +input. Returns 0 if the regular expression is not found and adding more data +wouldn't change anything. +)"}; + } +END_METHOD + +BEGIN_METHOD(regexp, FindSpan) + auto signature() const { + return Signature{.self = type::RegExp(), + .result = type::Tuple({type::SignedInteger(32), type::Bytes()}), + .id = "find_span", + .args = {{.id = "data", .type = type::constant(type::Bytes())}}, + .doc = R"( +Searches the regular expression in *data*. Returns a 2-tuple with (1) a integer +match indicator with the same semantics as that returned by ``find``; and (2) if a +match has been found, the data that matches the regular expression. +)"}; + } +END_METHOD + +BEGIN_METHOD(regexp, FindGroups) + auto signature() const { + return Signature{.self = type::RegExp(), + .result = type::Vector(type::Bytes()), + .id = "find_groups", + .args = {{.id = "data", .type = type::constant(type::Bytes())}}, + .doc = R"( +Searches the regular expression in *data*. If the regular expression is found, +returns a vector with one entry for each capture group defined by the regular +expression; starting at index 1. Each of these entries is a view locating the +matching bytes. In addition, index 0 always contains the data that matches +the full regular expression. Returns an empty vector if the expression is not +found. This method is not compatible with pattern sets and will throw a runtime +exception if used with a regular expression compiled from a set. +)"}; + } +END_METHOD + +BEGIN_METHOD(regexp, TokenMatcher) + auto signature() const { + return Signature{.self = type::RegExp(), + .result = type::Library("hilti::rt::regexp::MatchState"), + .id = "token_matcher", + .args = {}, + .doc = R"( +Initializes state for matching regular expression incrementally against chunks +of future input. The regular expression will be considered implicitly anchored. +The regular expression must have been compiled with the ``&nosub`` attribute. +)"}; + } +END_METHOD + +BEGIN_METHOD(regexp_match_state, AdvanceBytes) + auto signature() const { + return Signature{.self = type::Library("hilti::rt::regexp::MatchState"), + .result = type::Tuple({type::SignedInteger(32), type::stream::View()}), + .id = "advance", + .args = {{.id = "data", .type = type::constant(type::Bytes())}, + {.id = "final", + .type = type::Bool(), + .default_ = expression::Ctor(ctor::Bool(true))}}, + .doc = R"( +Feeds a chunk of data into the token match state, continuing matching where it +left off last time. If *final* is true, this is assumed to be the final piece +of data; any further advancing will then lead to an exception. Returns a +2-tuple with (1) a integer match indicator with the same semantics as that +returned by ``regexp::find()``; and (2) the number of bytes in *data* consumed +by the matching. The state must not be used again once an integer larger +or equal zero has been returned. +)"}; + } +END_METHOD + +BEGIN_METHOD(regexp_match_state, AdvanceView) + auto signature() const { + return Signature{.self = type::Library("hilti::rt::regexp::MatchState"), + .result = type::Tuple({type::SignedInteger(32), type::stream::View()}), + .id = "advance", + .args = {{.id = "data", .type = type::constant(type::stream::View())}}, + .doc = R"( +Feeds a chunk of data into the token match state, continuing matching where it +left off last time. If the underlying view is frozen, this will be assumed to +be last piece of data; any further advancing will then lead to an exception. +Returns a 2-tuple with (1) a integer match indicator with the same semantics as +that returned by ``regexp::find()``; and (2) a new view that's triming *data* +to the part not yet consumed. The state must not be used again once an integer +larger or equal zero has been returned. +)"}; + } +END_METHOD + +} // namespace hilti::operator_ diff --git a/hilti/include/ast/operators/result.h b/hilti/include/ast/operators/result.h new file mode 100644 index 000000000..3575434d3 --- /dev/null +++ b/hilti/include/ast/operators/result.h @@ -0,0 +1,28 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_1(result, Deref, operator_::dereferencedType(0), type::constant(type::Result(type::Wildcard())), + "Retrieves value stored inside the result instance. Will throw a ``NoResult`` exception if the " + "result is in an error state."); + +BEGIN_METHOD(result, Error) + auto signature() const { + return Signature{.self = type::Result(type::Wildcard()), + .result = type::Error(), + .id = "error", + .args = {}, + .doc = + "Retrieves the error stored inside the result instance. Will throw a ``NoError`` " + "exception if the result is not in an error state."}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/set.h b/hilti/include/ast/operators/set.h new file mode 100644 index 000000000..bccb072fb --- /dev/null +++ b/hilti/include/ast/operators/set.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_1(set::iterator, Deref, operator_::dereferencedType(0), + type::constant(type::set::Iterator(type::Wildcard())), + "Returns the set element that the iterator refers to."); +STANDARD_OPERATOR_1(set::iterator, IncrPostfix, operator_::sameTypeAs(0, "iterator>"), + type::set::Iterator(type::Wildcard()), + "Advances the iterator by one set element, returning the previous position."); +STANDARD_OPERATOR_1(set::iterator, IncrPrefix, operator_::sameTypeAs(0, "iterator>"), + type::set::Iterator(type::Wildcard()), + "Advances the iterator by one set element, returning the new position."); +STANDARD_OPERATOR_2(set::iterator, Equal, type::Bool(), type::constant(type::set::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two sets iterators refer to the same location."); +STANDARD_OPERATOR_2(set::iterator, Unequal, type::Bool(), type::constant(type::set::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two sets iterators refer to different locations."); + +STANDARD_OPERATOR_1(set, Size, type::UnsignedInteger(64), type::constant(type::Set(type::Wildcard())), + "Returns the number of elements a set contains."); +STANDARD_OPERATOR_2(set, Equal, type::Bool(), type::constant(type::Set(type::Wildcard())), + operator_::sameTypeAs(0, "set<*>"), "Compares two sets element-wise."); +STANDARD_OPERATOR_2(set, Unequal, type::Bool(), type::constant(type::Set(type::Wildcard())), + operator_::sameTypeAs(0, "set<*>"), "Compares two sets element-wise."); +STANDARD_OPERATOR_2(set, In, type::Bool(), type::Any(), type::constant(type::Set(type::Wildcard())), + "Returns true if an element is part of the set."); +STANDARD_OPERATOR_2(set, Add, type::Void(), type::Set(type::Wildcard()), operator_::constantElementType(0), + "Adds an element to the set.") +STANDARD_OPERATOR_2(set, Delete, type::Void(), type::Set(type::Wildcard()), operator_::constantElementType(0), + "Removes an element from the set.") + +BEGIN_METHOD(set, Clear) + auto signature() const { + return Signature{.self = type::Set(type::Wildcard()), + .result = type::Void(), + .id = "clear", + .args = {}, + .doc = R"( +Removes all elements from the set. +)"}; + } +END_METHOD + +} // namespace operator_ + +} // namespace hilti diff --git a/hilti/include/ast/operators/signed-integer.h b/hilti/include/ast/operators/signed-integer.h new file mode 100644 index 000000000..99bebd1ad --- /dev/null +++ b/hilti/include/ast/operators/signed-integer.h @@ -0,0 +1,111 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +namespace detail { +inline static auto widestTypeSigned() { + return [=](const std::vector& orig_ops, + const std::vector& resolved_ops) -> std::optional { + if ( orig_ops.empty() && resolved_ops.empty() ) + return type::DocOnly("uint<*>"); + + int w1 = 0; + int w2 = 0; + + if ( auto t = orig_ops[0].type().tryAs() ) + w1 = t->width(); + else if ( auto t = orig_ops[0].type().tryAs() ) + w1 = t->width(); + + if ( auto t = orig_ops[1].type().tryAs() ) + w2 = t->width(); + else if ( auto t = orig_ops[1].type().tryAs() ) + w2 = t->width(); + + if ( ! (w1 && w2) ) + return {}; + + const bool is_ctor1 = orig_ops[0].isA(); + const bool is_ctor2 = orig_ops[1].isA(); + + if ( is_ctor1 && ! is_ctor2 ) + return type::SignedInteger(w2); + + if ( is_ctor2 && ! is_ctor1 ) + return type::SignedInteger(w1); + + return type::SignedInteger(std::max(w1, w2)); + }; +} +} // namespace detail + +STANDARD_OPERATOR_1(signed_integer, DecrPostfix, operator_::sameTypeAs(0, "int"), type::SignedInteger(type::Wildcard()), + "Decrements the value, returning the old value."); +STANDARD_OPERATOR_1(signed_integer, DecrPrefix, operator_::sameTypeAs(0, "int"), type::SignedInteger(type::Wildcard()), + "Increments the value, returning the new value."); +STANDARD_OPERATOR_1(signed_integer, IncrPostfix, operator_::sameTypeAs(0, "int"), type::SignedInteger(type::Wildcard()), + "Increments the value, returning the old value."); +STANDARD_OPERATOR_1(signed_integer, IncrPrefix, operator_::sameTypeAs(0, "int"), type::SignedInteger(type::Wildcard()), + "Increments the value, returning the new value."); +STANDARD_OPERATOR_1(signed_integer, SignNeg, operator_::sameTypeAs(0, "int"), type::SignedInteger(type::Wildcard()), + "Inverts the sign of the integer."); +STANDARD_OPERATOR_2(signed_integer, Difference, detail::widestTypeSigned(), detail::widestTypeSigned(), + detail::widestTypeSigned(), "Returns the difference between the two integers."); +STANDARD_OPERATOR_2(signed_integer, DifferenceAssign, operator_::sameTypeAs(0, "int"), + type::SignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "int"), + "Decrements the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(signed_integer, Division, detail::widestTypeSigned(), detail::widestTypeSigned(), + detail::widestTypeSigned(), "Divides the first integer by the second."); +STANDARD_OPERATOR_2(signed_integer, DivisionAssign, operator_::sameTypeAs(0, "int"), + type::SignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "int"), + "Dividies the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(signed_integer, Equal, type::Bool(), detail::widestTypeSigned(), detail::widestTypeSigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(signed_integer, Greater, type::Bool(), detail::widestTypeSigned(), detail::widestTypeSigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(signed_integer, GreaterEqual, type::Bool(), detail::widestTypeSigned(), detail::widestTypeSigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(signed_integer, Lower, type::Bool(), detail::widestTypeSigned(), detail::widestTypeSigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(signed_integer, LowerEqual, type::Bool(), detail::widestTypeSigned(), detail::widestTypeSigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(signed_integer, Modulo, detail::widestTypeSigned(), detail::widestTypeSigned(), + detail::widestTypeSigned(), "Computes the modulus of the first integer divided by the second."); +STANDARD_OPERATOR_2(signed_integer, Multiple, detail::widestTypeSigned(), detail::widestTypeSigned(), + detail::widestTypeSigned(), "Multiplies the first integer by the second."); +STANDARD_OPERATOR_2(signed_integer, MultipleAssign, operator_::sameTypeAs(0, "int"), + type::SignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "int"), + "Multiplies the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(signed_integer, Power, detail::widestTypeSigned(), detail::widestTypeSigned(), + detail::widestTypeSigned(), "Computes the first integer raised to the power of the second."); +STANDARD_OPERATOR_2(signed_integer, Sum, detail::widestTypeSigned(), detail::widestTypeSigned(), + detail::widestTypeSigned(), "Returns the sum of the integers."); +STANDARD_OPERATOR_2(signed_integer, SumAssign, operator_::sameTypeAs(0, "int"), type::SignedInteger(type::Wildcard()), + operator_::sameTypeAs(0, "int"), + "Increments the first integer by the second, assigning the new value."); +STANDARD_OPERATOR_2(signed_integer, Unequal, type::Bool(), detail::widestTypeSigned(), detail::widestTypeSigned(), + "Compares the two integers."); + +STANDARD_OPERATOR_2x(signed_integer, CastUnsigned, Cast, operator_::typedType(1, "uint<*>"), + type::SignedInteger(type::Wildcard()), type::Type_(type::UnsignedInteger(type::Wildcard())), + "Converts the value to an unsigned integer type, accepting any loss of information."); +STANDARD_OPERATOR_2x(signed_integer, CastSigned, Cast, operator_::typedType(1, "int<*>"), + type::SignedInteger(type::Wildcard()), type::Type_(type::SignedInteger(type::Wildcard())), + "Converts the value to another signed integer type, accepting any loss of information."); +STANDARD_OPERATOR_2x(signed_integer, CastReal, Cast, operator_::typedType(1, "real"), + type::SignedInteger(type::Wildcard()), type::Type_(type::Real()), + "Converts the value into a real, accepting any loss of information."); + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/stream.h b/hilti/include/ast/operators/stream.h new file mode 100644 index 000000000..7a057222d --- /dev/null +++ b/hilti/include/ast/operators/stream.h @@ -0,0 +1,314 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +// stream::Iterator + +STANDARD_OPERATOR_1(stream::iterator, Deref, type::UnsignedInteger(64), type::constant(type::stream::Iterator()), + "Returns the byte the iterator is pointing to."); +STANDARD_OPERATOR_1(stream::iterator, IncrPostfix, type::stream::Iterator(), type::stream::Iterator(), + "Advances the iterator by one byte, returning the previous position."); +STANDARD_OPERATOR_1(stream::iterator, IncrPrefix, type::stream::Iterator(), type::stream::Iterator(), + "Advances the iterator by one byte, returning the new position."); + +STANDARD_OPERATOR_2( + stream::iterator, Equal, type::Bool(), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same stream value."); +STANDARD_OPERATOR_2( + stream::iterator, Unequal, type::Bool(), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same stream value."); +STANDARD_OPERATOR_2( + stream::iterator, Lower, type::Bool(), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same stream value."); +STANDARD_OPERATOR_2( + stream::iterator, LowerEqual, type::Bool(), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same stream value."); +STANDARD_OPERATOR_2( + stream::iterator, Greater, type::Bool(), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same stream value."); +STANDARD_OPERATOR_2( + stream::iterator, GreaterEqual, type::Bool(), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Compares the two positions. The result is undefined if they are not refering to the same stream value."); +STANDARD_OPERATOR_2( + stream::iterator, Difference, type::SignedInteger(64), type::constant(type::stream::Iterator()), + type::constant(type::stream::Iterator()), + "Returns the number of stream between the two iterators. The result will be negative if the second iterator points " + "to a location before the first. The result is undefined if the iterators do not refer to the same stream " + "instace."); +STANDARD_OPERATOR_2(stream::iterator, Sum, type::stream::Iterator(), type::constant(type::stream::Iterator()), + type::UnsignedInteger(64), "Advances the iterator by the given number of stream.") +STANDARD_OPERATOR_2(stream::iterator, SumAssign, type::stream::Iterator(), type::stream::Iterator(), + type::UnsignedInteger(64), "Advances the iterator by the given number of stream.") + +BEGIN_METHOD(stream::iterator, Offset) + auto signature() const { + return Signature{.self = type::constant(type::stream::Iterator()), + .result = type::UnsignedInteger(64), + .id = "offset", + .args = {}, + .doc = R"( +Returns the offset of the byte that the iterator refers to relative to the +beginning of the underlying stream value. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::iterator, IsFrozen) + auto signature() const { + return Signature{.self = type::constant(type::stream::Iterator()), + .result = type::Bool(), + .id = "is_frozen", + .args = {}, + .doc = R"( +Returns whether the stream value that the iterator referes to has been frozen. +)"}; + } +END_METHOD + +// stream::View + +STANDARD_OPERATOR_1(stream::view, Size, type::UnsignedInteger(64), type::constant(type::stream::View()), + "Returns the number of stream the view contains."); +STANDARD_OPERATOR_2x(stream::view, InBytes, In, type::Bool(), type::constant(type::stream::View()), + type::constant(type::Bytes()), + "Returns true if the right-hand-side view contains the left-hand-side bytes as a subsequence."); +STANDARD_OPERATOR_2x(stream::view, InView, In, type::Bool(), type::constant(type::Bytes()), + type::constant(type::stream::View()), + "Returns true if the right-hand-side bytes contains the left-hand-side view as a subsequence."); +STANDARD_OPERATOR_2x(stream::view, EqualView, Equal, type::Bool(), type::constant(type::stream::View()), + type::constant(type::stream::View()), "Compares the views lexicographically."); +STANDARD_OPERATOR_2x(stream::view, EqualBytes, Equal, type::Bool(), type::constant(type::stream::View()), + type::constant(type::Bytes()), "Compares a stream view and a bytes intances lexicographically."); +STANDARD_OPERATOR_2x(stream::view, UnequalView, Unequal, type::Bool(), type::constant(type::stream::View()), + type::constant(type::stream::View()), "Compares two views lexicographically."); +STANDARD_OPERATOR_2x(stream::view, UnequalBytes, Unequal, type::Bool(), type::constant(type::stream::View()), + type::constant(type::Bytes()), "Compares a stream view and a bytes instance lexicographically."); + +BEGIN_METHOD(stream::view, Offset) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::UnsignedInteger(64), + .id = "offset", + .args = {}, + .doc = R"( +Returns the offset of the view's starting position within the associated stream value. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, AdvanceBy) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::View(), + .id = "advance", + .args = {{.id = "i", .type = type::stream::Iterator()}}, + .doc = R"( +Advances the view's starting position to a given iterator *i*, returning the new +view. The iterator must be refering to the same stream values as the view, and +it must be equal or ahead of the view's starting position. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, Limit) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::View(), + .id = "limit", + .args = {{.id = "i", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Returns a new view that keeps the current start but cuts off the end *i* +characters from that beginning. The returned view will not be able to expand any +further. The iterator must be refering to the same stream values as the view, +and it must be inside the current view's range. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, AdvanceTo) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::View(), + .id = "advance", + .args = {{.id = "i", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Advances the view's starting position by *i* stream, returning the new view. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, Find) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::Tuple({type::Bool(), type::stream::Iterator()}), + .id = "find", + .args = {{.id = "needle", .type = type::constant(type::Bytes())}}, + .doc = R"( +Searches *needle* inside the view's content. Returns a tuple of a boolean and an +iterator. If *needle* was found, the boolean will be true and the iterator will point +to its first occurance. If *needle* was not found, the boolean will be false and +the iterator will point to the last position so that everything before that is +guaranteed to not contain even a partial match of *needle* (in other words: one can +``trim`` until that position and then restart the search from there if more data +gets appended to the underlying stream value). Note that for a simple yes/no result, +you should use the ``in`` operator instead of this method, as it's more efficient. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, At) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::Iterator(), + .id = "at", + .args = {{.id = "i", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Returns an iterator representing the offset *i* inside the view. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, StartsWith) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::Bool(), + .id = "starts_with", + .args = {{.id = "b", .type = type::constant(type::Bytes())}}, + .doc = R"( +Returns true if the view starts with *b*. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, SubIterators) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::View(), + .id = "sub", + .args = {{.id = "begin", .type = type::stream::Iterator()}, + {.id = "end", .type = type::stream::Iterator()}}, + .doc = R"( +Returns a new view of the subsequence from *begin* to (but not including) +*end*. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, SubIterator) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::View(), + .id = "sub", + .args = {{.id = "end", .type = type::stream::Iterator()}}, + .doc = R"( +Returns a new view of the subsequence from *begin* to (but not including) +*end*. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream::view, SubOffsets) + auto signature() const { + return Signature{.self = type::constant(type::stream::View()), + .result = type::stream::View(), + .id = "sub", + .args = {{.id = "begin", .type = type::UnsignedInteger(64)}, + {.id = "end", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Returns a new view of the subsequence from offset *begin* to (but not including) +offset *end*. The offsets are relative to the beginning of the view. +)"}; + } +END_METHOD + +// Stream + +STANDARD_OPERATOR_1(stream, Size, type::UnsignedInteger(64), type::constant(type::Stream()), + "Returns the number of stream the value contains."); +STANDARD_OPERATOR_2(stream, Unequal, type::Bool(), type::constant(type::Stream()), type::constant(type::Stream()), + "Compares two stream values lexicographically."); +STANDARD_OPERATOR_2x(stream, SumAssignView, SumAssign, type::Stream(), type::Stream(), + type::constant(type::stream::View()), "Concatenates another stream's view to the target stream."); +STANDARD_OPERATOR_2x(stream, SumAssignBytes, SumAssign, type::Stream(), type::Stream(), type::constant(type::Bytes()), + "Concatenates data to the stream."); + +BEGIN_METHOD(stream, Freeze) + auto signature() const { + return Signature{.self = type::Stream(), .result = type::Void(), .id = "freeze", .args = {}, .doc = R"( +Freezes the stream value. Once frozen, one cannot append any more data to a +frozen stream value (unless it gets unfrozen first). If the value is +already frozen, the operation does not change anything. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream, Unfreeze) + auto signature() const { + return Signature{.self = type::Stream(), .result = type::Void(), .id = "unfreeze", .args = {}, .doc = R"( +Unfreezes the stream value. A unfrozen stream value can be further modified. If +the value is already unfrozen (which is the default), the operation does not +change anything. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream, IsFrozen) + auto signature() const { + return Signature{.self = type::constant(type::Stream()), + .result = type::Bool(), + .id = "is_frozen", + .args = {}, + .doc = R"( +Returns true if the stream value has been frozen. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream, At) + auto signature() const { + return Signature{.self = type::constant(type::Stream()), + .result = type::stream::Iterator(), + .id = "at", + .args = {{.id = "i", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Returns an iterator representing the offset *i* inside the stream value. +)"}; + } +END_METHOD + +BEGIN_METHOD(stream, Trim) + auto signature() const { + return Signature{.self = type::Stream(), + .result = type::Void(), + .id = "trim", + .args = {{.id = "i", .type = type::stream::Iterator()}}, + .doc = R"( +Trims the stream value by removing all data from its beginning up to (but not +including) the position *i*. The iterator *i* will remain valid afterwards and +will still point to the same location, which will now be the beginning of the stream +value. All existigng iterators poiting to *i* or beyond will remain valid and keep +their offsets as well. The effect of this operation is undefined if *i* does not +actually refer to a location inside the stream value. Trimming is permitted +even on frozen values. +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/string.h b/hilti/include/ast/operators/string.h new file mode 100644 index 000000000..3707430fb --- /dev/null +++ b/hilti/include/ast/operators/string.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(string, Equal, type::Bool(), type::String(), type::String(), + "Compares two strings lexicographically.") +STANDARD_OPERATOR_2(string, Unequal, type::Bool(), type::String(), type::String(), + "Compares two strings lexicographically.") +STANDARD_OPERATOR_1(string, Size, type::UnsignedInteger(64), type::String(), + "Returns the number of characters the string contains."); +STANDARD_OPERATOR_2(string, Sum, type::String(), type::String(), type::String(), + "Returns the concatentation of two strings."); + +BEGIN_OPERATOR_CUSTOM(string, Modulo) + Type result(const std::vector& /* ops */) const { return type::String(); } + + bool isLhs() const { return false; } + + std::vector operands() const { return {{.type = type::String()}, {.type = type::Any()}}; } + + void validate(const expression::ResolvedOperator& /* i */, operator_::const_position_t /* p */) const { + // TODO(robin): Not sure if we need this restriction. Let's try without. + // + // if ( i.op1().type().isA() && ! i.op1().isA() ) + // logger().error("tuple argument to '%' must a be constant", i.op1()); + } + + std::string doc() const { return "Renders a printf-style format string."; } +END_OPERATOR_CUSTOM + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/struct.h b/hilti/include/ast/operators/struct.h new file mode 100644 index 000000000..64d4711b9 --- /dev/null +++ b/hilti/include/ast/operators/struct.h @@ -0,0 +1,197 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +namespace struct_::detail { + +// Returns an operand as a member expression. +static expression::Member memberExpression(const Expression& op) { + if ( auto c = op.tryAs() ) + return c->expression().as(); + + return op.as(); +} + +// Checks if an operand refers to a valid field inside a struct. +static inline void checkName(const Expression& op0, const Expression& op1) { + auto id = memberExpression(op1).id().local(); + + if ( auto f = op0.type().as().field(id); ! f ) + logger().error(util::fmt("type does not have field '%s'", id), op0); +} + +// Returns the type of a struct field referenced by an operand. +static inline Type itemType(const Expression& op0, const Expression& op1) { + if ( auto st = op0.type().tryAs() ) { + if ( auto f = st->field(memberExpression(op1).id().local()) ) + return f->type(); + } + + return type::unknown; +} + +} // namespace struct_::detail + +BEGIN_OPERATOR_CUSTOM_x(struct_, MemberNonConst, Member) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return true; } + + std::vector operands() const { + return {{.type = type::Struct(type::Wildcard()), .doc = "struct"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return R"( +Retrieves the value of a struct's field. If the field does not have a value assigned, +it returns its ``&default`` expression if that has been defined; otherwise it +triggers an exception. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM_x(struct_, MemberConst, Member) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::constant(type::Struct(type::Wildcard())), .doc = "struct"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return R"( +Retrieves the value of a struct's field. If the field does not have a value assigned, +it returns its ``&default`` expression if that has been defined; otherwise it +triggers an exception. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM(struct_, TryMember) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Struct(type::Wildcard()), .doc = "struct"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return R"( +Retrieves the value of a struct's field. If the field does not have a value +assigned, it returns its ``&default`` expression if that has been defined; +otherwise it signals a special non-error exception to the host application +(which will normally still lead to aborting execution, similar to the standard +dereference operator, unless the host application specifically handles this +exception differently). +)"; + } +END_OPERATOR_CUSTOM + +BEGIN_OPERATOR_CUSTOM(struct_, HasMember) + Type result(const std::vector& /* ops */) const { return type::Bool(); } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Struct(type::Wildcard()), .doc = "struct"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return "Returns true if the struct's field has a value assigned (not counting any ``&default``)."; + } +END_OPERATOR_CUSTOM + +OPERATOR_DECLARE_ONLY(struct_, MemberCall) + +namespace struct_ { + +class MemberCall : public hilti::expression::ResolvedOperatorBase { +public: + using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + + struct Operator : public hilti::trait::isOperator { + Operator(const type::Struct& stype, const type::struct_::Field& f) { + auto ftype = f.type().as(); + auto op0 = operator_::Operand{.type = stype}; + auto op1 = operator_::Operand{.type = type::Member(f.id())}; + auto op2 = operator_::Operand{.type = ftype.operands()}; + _field = f; + _operands = {op0, op1, op2}; + _result = ftype.result().type(); + }; + + static operator_::Kind kind() { return operator_::Kind::MemberCall; } + std::vector operands() const { return _operands; } + Type result(const std::vector& /* ops */) const { return _result; } + bool isLhs() const { return false; } + void validate(const expression::ResolvedOperator& /* i */, operator_::const_position_t /* p */) const {} + std::string doc() const { return ""; } + std::string docNamespace() const { return ""; } + + Expression instantiate(const std::vector& operands, const Meta& meta) const { + auto ops = + std::vector{operands[0], expression::Member(_field.id(), _field.type(), _field.meta()), + operands[2]}; + + auto ro = expression::ResolvedOperator(MemberCall(*this, ops, meta)); + ro.setMeta(meta); + return ro; + } + + private: + type::struct_::Field _field; + std::vector _operands; + Type _result; + }; +}; + +} // namespace struct_ + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/time.h b/hilti/include/ast/operators/time.h new file mode 100644 index 000000000..963e123d5 --- /dev/null +++ b/hilti/include/ast/operators/time.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(time, Equal, type::Bool(), type::Time(), type::Time(), "Compares two time values.") +STANDARD_OPERATOR_2(time, Unequal, type::Bool(), type::Time(), type::Time(), "Compares two time values.") +STANDARD_OPERATOR_2x(time, SumTime, Sum, type::Time(), type::Time(), type::Time(), "Returns the sum of the times."); +STANDARD_OPERATOR_2x(time, SumInterval, Sum, type::Time(), type::Time(), type::Interval(), + "Adds the interval to the time."); +STANDARD_OPERATOR_2x(time, DifferenceTime, Difference, type::Time(), type::Time(), type::Time(), + "Returns the difference of the times."); +STANDARD_OPERATOR_2x(time, DifferenceInterval, Difference, type::Time(), type::Time(), type::Interval(), + "Subtracts the interval from the time."); +STANDARD_OPERATOR_2(time, Greater, type::Bool(), type::Time(), type::Time(), "Compares the times."); +STANDARD_OPERATOR_2(time, GreaterEqual, type::Bool(), type::Time(), type::Time(), "Compares the times."); +STANDARD_OPERATOR_2(time, Lower, type::Bool(), type::Time(), type::Time(), "Compares the times."); +STANDARD_OPERATOR_2(time, LowerEqual, type::Bool(), type::Time(), type::Time(), "Compares the times."); +STANDARD_OPERATOR_2x(time, CastFromReal, Cast, type::Time(), type::Real(), type::Type_(type::Time()), + "Interprets the real value as number of seconds since the UNIX epoch."); +STANDARD_OPERATOR_2x(time, CastFromUnsignedInteger, Cast, type::Time(), type::UnsignedInteger(type::Wildcard()), + type::Type_(type::Time()), + "Interprets the unsigned integer value as number of seconds since the UNIX epoch."); + +BEGIN_METHOD(time, Seconds) + auto signature() const { + return Signature{.self = type::Time(), .result = type::Real(), .id = "seconds", .args = {}, .doc = R"( +Returns the time as a real value representing seconds since the UNIX epoch. +)"}; + } +END_METHOD + +BEGIN_METHOD(time, Nanoseconds) + auto signature() const { + return Signature{.self = type::Time(), .result = type::Real(), .id = "nanoseconds", .args = {}, .doc = R"( +Returns the time as an integer value representing nanoseconds since the UNIX epoch. +)"}; + } +END_METHOD + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/tuple.h b/hilti/include/ast/operators/tuple.h new file mode 100644 index 000000000..21be41527 --- /dev/null +++ b/hilti/include/ast/operators/tuple.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_2(tuple, Equal, type::Bool(), type::constant(type::Tuple(type::Wildcard())), + operator_::sameTypeAs(0, "tuple<*>"), "Compares two tuples element-wise."); +STANDARD_OPERATOR_2(tuple, Unequal, type::Bool(), type::constant(type::Tuple(type::Wildcard())), + operator_::sameTypeAs(0, "tuple<*>"), "Compares two tuples element-wise."); + +BEGIN_OPERATOR_CUSTOM(tuple, Index) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + auto i = ops[1].as().ctor().as(); + return ops[0].type().as().types()[i.value()]; + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Tuple(type::Wildcard())}, {.type = type::UnsignedInteger(64)}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + if ( auto ec = i.op1().tryAs() ) + if ( auto c = ec->ctor().tryAs() ) { + if ( c->value() < 0 || c->value() >= i.op0().type().as().types().size() ) + logger().error("tuple index out of range", i); + + return; + } + + logger().error("tuple index must be an integer constant", i); + } + + std::string doc() const { + return "Extracts the tuple element at the given index. The index must be a constant unsigned integer."; + } +END_OPERATOR_CUSTOM + +BEGIN_OPERATOR_CUSTOM(tuple, Member) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + auto id = ops[1].as().id(); + auto elem = ops[0].type().as().elementByID(id); + assert(elem); + return elem->second; + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Tuple(type::Wildcard())}, {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + auto id = i.operands()[1].as().id(); + auto elem = i.operands()[0].type().as().elementByID(id); + + if ( ! elem ) + logger().error("unknown tuple element", i); + } + + std::string doc() const { return "Extracts the tuple element corresponding to the given ID."; } +END_OPERATOR_CUSTOM_x + + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/union.h b/hilti/include/ast/operators/union.h new file mode 100644 index 000000000..15b73d34c --- /dev/null +++ b/hilti/include/ast/operators/union.h @@ -0,0 +1,129 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +namespace union_::detail { + +// Returns an operand as a member expression. +static expression::Member memberExpression(const Expression& op) { + if ( auto c = op.tryAs() ) + return c->expression().as(); + + return op.as(); +} + +// Checks if an operand refers to a valid field inside a union. +static inline void checkName(const Expression& op0, const Expression& op1) { + auto id = memberExpression(op1).id().local(); + + if ( auto f = op0.type().as().field(id); ! f ) + logger().error(util::fmt("type does not have field '%s'", id)); +} + +// Returns the type of a union field referenced by an operand. +static inline Type itemType(const Expression& op0, const Expression& op1) { + if ( auto st = op0.type().tryAs() ) { + if ( auto f = st->field(memberExpression(op1).id().local()) ) + return f->type(); + } + + return type::unknown; +} + +// Returns the result type of a union method referenced by an operand. +static inline Type methodResult(const Expression& /* op0 */, const Expression& op1) { + if ( auto f = memberExpression(op1).memberType()->template tryAs() ) + return f->result().type(); + + return type::unknown; +} + +} // namespace union_::detail + +STANDARD_OPERATOR_2(union_, Equal, type::Bool(), type::constant(type::Union(type::Wildcard())), + operator_::sameTypeAs(0, "union<*>"), "Compares two unions element-wise."); +STANDARD_OPERATOR_2(union_, Unequal, type::Bool(), type::constant(type::Union(type::Wildcard())), + operator_::sameTypeAs(0, "union<*>"), "Compares two unions element-wise."); + +BEGIN_OPERATOR_CUSTOM_x(union_, MemberConst, Member) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::constant(type::Union(type::Wildcard())), .doc = "union"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return R"( +Retrieves the value of a union's field. If the union does not have the field set, +this triggers an exception. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM_x(union_, MemberNonConst, Member) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return true; } + + std::vector operands() const { + return {{.type = type::Union(type::Wildcard()), .doc = "union"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return R"( +Retrieves the value of a union's field. If the union does not have the field set, +this triggers an exception unless the value is only being assigned to. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM(union_, HasMember) + Type result(const std::vector& /* ops */) const { return type::Bool(); } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Union(type::Wildcard()), .doc = "union"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const expression::ResolvedOperator& i, operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { return "Returns true if the union's field is set."; } +END_OPERATOR_CUSTOM + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/unsigned-integer.h b/hilti/include/ast/operators/unsigned-integer.h new file mode 100644 index 000000000..acd708b36 --- /dev/null +++ b/hilti/include/ast/operators/unsigned-integer.h @@ -0,0 +1,121 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +namespace detail { +inline static auto widestTypeUnsigned() { + return [=](const std::vector& orig_ops, + const std::vector& resolved_ops) -> std::optional { + if ( orig_ops.empty() && resolved_ops.empty() ) + return type::DocOnly("uint<*>"); + + int w1 = 0; + int w2 = 0; + + if ( auto t = orig_ops[0].type().tryAs() ) + w1 = t->width(); + else if ( auto t = orig_ops[0].type().tryAs() ) + w1 = t->width(); + + if ( auto t = orig_ops[1].type().tryAs() ) + w2 = t->width(); + else if ( auto t = orig_ops[1].type().tryAs() ) + w2 = t->width(); + + if ( ! (w1 && w2) ) + return {}; + + const bool is_ctor1 = orig_ops[0].isA(); + const bool is_ctor2 = orig_ops[1].isA(); + + if ( is_ctor1 && ! is_ctor2 ) + return type::UnsignedInteger(w2); + + if ( is_ctor2 && ! is_ctor1 ) + return type::UnsignedInteger(w1); + + return type::UnsignedInteger(std::max(w1, w2)); + }; +} +} // namespace detail + +STANDARD_OPERATOR_1(unsigned_integer, DecrPostfix, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), "Decrements the value, returning the old value."); +STANDARD_OPERATOR_1(unsigned_integer, DecrPrefix, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), "Increments the value, returning the new value."); +STANDARD_OPERATOR_1(unsigned_integer, IncrPostfix, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), "Increments the value, returning the old value."); +STANDARD_OPERATOR_1(unsigned_integer, IncrPrefix, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), "Increments the value, returning the new value."); +STANDARD_OPERATOR_1(unsigned_integer, Negate, operator_::sameTypeAs(0, "uint"), type::UnsignedInteger(type::Wildcard()), + "Computes the bit-wise negation of the integer."); +STANDARD_OPERATOR_2(unsigned_integer, BitAnd, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the bit-wise 'and' of the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, BitOr, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the bit-wise 'or' of the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, BitXor, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the bit-wise 'xor' of the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, Difference, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the difference between the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, DifferenceAssign, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "uint"), + "Decrements the first value by the second."); +STANDARD_OPERATOR_2(unsigned_integer, Division, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Divides the first integer by the second."); +STANDARD_OPERATOR_2(unsigned_integer, DivisionAssign, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "uint"), + "Dividies the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(unsigned_integer, Equal, type::Bool(), detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, Greater, type::Bool(), detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, GreaterEqual, type::Bool(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Compares the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, Lower, type::Bool(), detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + "Compares the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, LowerEqual, type::Bool(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Compares the two integers."); +STANDARD_OPERATOR_2(unsigned_integer, Modulo, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the modulus of the first integer divided by the second."); +STANDARD_OPERATOR_2(unsigned_integer, Multiple, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Multiplies the first integer by the second."); +STANDARD_OPERATOR_2(unsigned_integer, MultipleAssign, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "uint"), + "Multiplies the first value by the second, assigning the new value."); +STANDARD_OPERATOR_2(unsigned_integer, Power, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the first integer raised to the power of the second."); +STANDARD_OPERATOR_2(unsigned_integer, ShiftLeft, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), type::UnsignedInteger(type::Wildcard()), + "Shifts the integer to the left by the given number of bits."); +STANDARD_OPERATOR_2(unsigned_integer, ShiftRight, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), type::UnsignedInteger(type::Wildcard()), + "Shifts the integer to the right by the given number of bits."); +STANDARD_OPERATOR_2(unsigned_integer, Sum, detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + detail::widestTypeUnsigned(), "Computes the sum of the integers."); +STANDARD_OPERATOR_2(unsigned_integer, SumAssign, operator_::sameTypeAs(0, "uint"), + type::UnsignedInteger(type::Wildcard()), operator_::sameTypeAs(0, "uint"), + "Increments the first value by the second."); +STANDARD_OPERATOR_2(unsigned_integer, Unequal, type::Bool(), detail::widestTypeUnsigned(), detail::widestTypeUnsigned(), + "Compares the two integers."); + +STANDARD_OPERATOR_2x(unsigned_integer, CastUnsigned, Cast, operator_::typedType(1, "uint<*>"), + type::UnsignedInteger(type::Wildcard()), type::Type_(type::UnsignedInteger(type::Wildcard())), + "Converts the value to another unsigned integer type, accepting any loss of information."); +STANDARD_OPERATOR_2x(unsigned_integer, CastSigned, Cast, operator_::typedType(1, "int<*>"), + type::UnsignedInteger(type::Wildcard()), type::Type_(type::SignedInteger(type::Wildcard())), + "Converts the value to signed integer type, accepting any loss of information."); +STANDARD_OPERATOR_2x(unsigned_integer, CastReal, Cast, operator_::typedType(1, "real"), + type::UnsignedInteger(type::Wildcard()), type::Type_(type::Real()), + "Converts the value into a real, accepting any loss of information."); + +} // namespace operator_ +} // namespace hilti diff --git a/hilti/include/ast/operators/vector.h b/hilti/include/ast/operators/vector.h new file mode 100644 index 000000000..c6a234490 --- /dev/null +++ b/hilti/include/ast/operators/vector.h @@ -0,0 +1,103 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace operator_ { + +STANDARD_OPERATOR_1(vector::iterator, Deref, operator_::dereferencedType(0), + type::constant(type::vector::Iterator(type::Wildcard())), + "Returns the vector element that the iterator refers to."); +STANDARD_OPERATOR_1(vector::iterator, IncrPostfix, operator_::sameTypeAs(0, "iterator>"), + type::vector::Iterator(type::Wildcard()), + "Advances the iterator by one vector element, returning the previous position."); +STANDARD_OPERATOR_1(vector::iterator, IncrPrefix, operator_::sameTypeAs(0, "iterator>"), + type::vector::Iterator(type::Wildcard()), + "Advances the iterator by one vector element, returning the new position."); +STANDARD_OPERATOR_2(vector::iterator, Equal, type::Bool(), type::constant(type::vector::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two vectors iterators refer to the same location."); +STANDARD_OPERATOR_2(vector::iterator, Unequal, type::Bool(), type::constant(type::vector::Iterator(type::Wildcard())), + operator_::sameTypeAs(0, "iterator>"), + "Returns true if two vectors iterators refer to different locations."); + +STANDARD_OPERATOR_1(vector, Size, type::UnsignedInteger(64), type::constant(type::Vector(type::Wildcard())), + "Returns the number of elements a vector contains."); +STANDARD_OPERATOR_2(vector, Equal, type::Bool(), type::constant(type::Vector(type::Wildcard())), + operator_::sameTypeAs(0, "vector<*>"), "Compares two vectors element-wise."); +STANDARD_OPERATOR_2x(vector, IndexConst, Index, operator_::constantElementType(0), + type::constant(type::Vector(type::Wildcard())), type::UnsignedInteger(64), + "Returns the vector element at the given index."); +STANDARD_OPERATOR_2x_lhs(vector, IndexNonConst, Index, operator_::elementType(0), type::Vector(type::Wildcard()), + type::UnsignedInteger(64), "Returns the vector element at the given index."); +STANDARD_OPERATOR_2(vector, Unequal, type::Bool(), type::constant(type::Vector(type::Wildcard())), + operator_::sameTypeAs(0, "vector<*>"), "Compares two vectors element-wise."); + +STANDARD_OPERATOR_2(vector, Sum, operator_::sameTypeAs(0, "vector<*>"), type::Vector(type::Wildcard()), + operator_::sameTypeAs(0, "vector<*>"), "Returns the concatentation of two vectors.") +STANDARD_OPERATOR_2(vector, SumAssign, operator_::sameTypeAs(0, "vector<*>"), type::Vector(type::Wildcard()), + operator_::sameTypeAs(0, "vector<*>"), "Concatenates another vector to the vector.") + +BEGIN_METHOD(vector, PushBack) + auto signature() const { + return Signature{.self = type::Vector(type::Wildcard()), + .result = type::Void(), + .id = "push_back", + .args = {{.id = "x", .type = type::Any()}}, + .doc = R"( +Appends *x* to the end of the vector. +)"}; + } +END_METHOD + +BEGIN_METHOD(vector, Front) + auto signature() const { + return Signature{.self = type::constant(type::Vector(type::Wildcard())), + .result = operator_::constantElementType(0), + .id = "front", + .args = {}, + .doc = R"( +Returns the first element of the vector. It throws an exception if the vector is +empty. +)"}; + } +END_METHOD + + +BEGIN_METHOD(vector, Back) + auto signature() const { + return Signature{.self = type::constant(type::Vector(type::Wildcard())), + .result = operator_::constantElementType(0), + .id = "back", + .args = {}, + .doc = R"( +Returns the last element of the vector. It throws an exception if the vector is +empty. +)"}; + } +END_METHOD + +BEGIN_METHOD(vector, Reserve) + auto signature() const { + return Signature{.self = type::Vector(type::Wildcard()), + .result = type::Void(), + .id = "reserve", + .args = {{.id = "n", .type = type::constant(type::UnsignedInteger(64))}}, + .doc = R"( +Reserves space for at least *n* elements. This operation does not change the +vector in any observable way but provides a hint about the size that will be +needed. +)"}; + } +END_METHOD + +} // namespace operator_ + +} // namespace hilti diff --git a/hilti/include/ast/scope.h b/hilti/include/ast/scope.h new file mode 100644 index 000000000..c959c81ea --- /dev/null +++ b/hilti/include/ast/scope.h @@ -0,0 +1,112 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti { + +class ID; + +/** + * Identifier scope. A scope maps identifiers to AST nodes (more precisely: + * to referneces to AST nodes). An identifier can be mapped to more than one + * node. + */ +class Scope { +public: + Scope() = default; + ~Scope() = default; + + /** + * Inserts a new identifier mapping. If a mapping for the ID already + * exists, the new one is appended to it. + * + * @param id id to map + * @param n reference to the node that `id` is to be mapped to + */ + void insert(const ID& id, NodeRef n); + + /** + * Inserts a new identifier mapping. + * + * @param id id to map + * @param n node to map to; as a scope always maps to *references*, + * this takes ownership of the node and stores it internally + */ + void insert(const ID& id, Node&& n); + + /** Returns true if there's at least one mapping for an ID. */ + bool has(const ID& id) const { return ! _findID(id).empty(); } + + /** Result typer for the lookup methods. */ + struct Referee { + NodeRef node; /**< node that ID maps to */ + std::string qualified; /**< qualified ID with full path used to find it */ + bool external{}; /**< true if found in a different (imported) module */ + }; + + /** Returns all mappings for an ID. */ + std::vector lookupAll(const ID& id) const { return _findID(id); } + + /** Returns first mapping for an ID. */ + std::optional lookup(const ID& id) const { + if ( auto ids = _findID(id); ! ids.empty() ) + return ids.front(); + + return {}; + } + + /** Empties the scope. */ + void clear() { _items.clear(); } + + /** Returns all mappings of the scope. */ + const auto& items() const { return _items; } + + /** + * Copies the scope's mappings into another one. + */ + void copyInto(Scope* dst) const { + for ( const auto& i : _items ) + dst->_items.insert(i); + } + + /** + * Moves the scope's mappings into another one. The source scope will be + * empty afterwards. + */ + void moveInto(Scope* dst) { + // dst->_items.merge(std::move(_items)); // C++17, not supported by libc++ yet it seems + for ( const auto& i : _items ) + dst->_items.insert(i); + + _items.clear(); + } + + /** + * Prints out a debugging representation of the scope's content. + * + * @param out stream to print to + * @param prefix string to prefix each printed scope item with + */ + void render(std::ostream& out, const std::string& prefix = "") const; + + Scope(const Scope& other) = delete; + Scope(Scope&& other) = delete; + Scope& operator=(const Scope& other) = delete; + Scope& operator=(Scope&& other) = delete; + +private: + using ItemMap = std::map>; + + std::vector _findID(const ID& id, bool external = false) const; + std::vector _findID(const Scope* scope, const ID& id, bool external = false) const; + + ItemMap _items; + std::vector> _nodes; // Nodes without other owners. +}; + +} // namespace hilti diff --git a/hilti/include/ast/statement.api b/hilti/include/ast/statement.api new file mode 100644 index 000000000..da00b9ac2 --- /dev/null +++ b/hilti/include/ast/statement.api @@ -0,0 +1,28 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for HILTI statements. */ +class Statement(trait::isStatement) : trait::isNode { + /** Returns true if the statement is equivalent to another one in HILTI semantics. */ + bool isEqual(const Statement& other) const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + + /** Implements the `Node` interface. */ + const std::vector& childs() const; + + /** Implements the `Node` interface. */ + std::vector& childs(); + + /** Implements the `Node` interface. */ + const Meta& meta() const; + + /** Implements the `Node` interface. */ + void setMeta(Meta m); + + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/hilti/include/ast/statement.h b/hilti/include/ast/statement.h new file mode 100644 index 000000000..6877a3524 --- /dev/null +++ b/hilti/include/ast/statement.h @@ -0,0 +1,47 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { + +namespace trait { +/** Trait for classes implementing the `Statement` interface. */ +class isStatement : public isNode {}; +} // namespace trait + +namespace statement { +namespace detail { +#include + +/** Creates an AST node representing a `Statement`. */ +inline Node to_node(Statement t) { return Node(std::move(t)); } + +/** Renders a statement as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, Statement s) { return out << to_node(std::move(s)); } + +inline bool operator==(const Statement& x, const Statement& y) { + if ( &x == &y ) + return true; + + assert(x.isEqual(y) == y.isEqual(x)); // Expected to be symmetric. + return x.isEqual(y); +} + +inline bool operator!=(const Statement& s1, const Statement& s2) { return ! (s1 == s2); } + +} // namespace detail +} // namespace statement + +using Statement = statement::detail::Statement; +using statement::detail::to_node; + +/** Constructs an AST node from any class implementing the `Statement` interface. */ +template::value>* = nullptr> +inline Node to_node(T t) { + return Node(Statement(std::move(t))); +} + +} // namespace hilti diff --git a/hilti/include/ast/statements/all.h b/hilti/include/ast/statements/all.h new file mode 100644 index 000000000..e84f7958c --- /dev/null +++ b/hilti/include/ast/statements/all.h @@ -0,0 +1,19 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/statements/assert.h b/hilti/include/ast/statements/assert.h new file mode 100644 index 000000000..f2a8879ef --- /dev/null +++ b/hilti/include/ast/statements/assert.h @@ -0,0 +1,80 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +namespace assert { +/** + * Tag for `Assert` to constructor to create an assertion expecting an + * exception to occur. + */ +struct Exception {}; +} // namespace assert + +/** AST node for an assert statement. */ +class Assert : public NodeBase, public hilti::trait::isStatement { +public: + /** + * Creates an assert statement that expects an exception to evaluate to true at runtime. + * + * @param e expression to evaluate at runtime + * @param msg message to report an runtime if assertions fails + * @param m meta informatio for AST node + */ + Assert(::hilti::Expression expr, std::optional<::hilti::Expression> msg, Meta m = Meta()) + : NodeBase(nodes(std::move(expr), node::none, std::move(msg)), std::move(m)) {} + + /** + * Creates an assert statement that expects an exception to occur when + * the expression is evaluated. + * + * @param assert::Exception tag to select this constructor + * @param e expression to evaluate at runtime + * @param type exception type that's expected to be thrown when *e* is evaluated; unset of any exception + * @param msg message to report an runtime if assertions fails + * @param m meta informatio for AST node + */ + Assert(assert::Exception /*unused*/, ::hilti::Expression expr, std::optional excpt, + std::optional<::hilti::Expression> msg, Meta m = Meta()) + : NodeBase(nodes(std::move(expr), std::move(excpt), std::move(msg)), std::move(m)), _expects_exception(true) {} + + bool expectsException() const { return _expects_exception; } + auto expression() const { return child<::hilti::Expression>(0); } + auto exception() const { return type::effectiveOptionalType(childs()[1].tryAs()); } + auto message() const { return childs()[2].tryAs<::hilti::Expression>(); } + + bool operator==(const Assert& other) const { + return _expects_exception == other._expects_exception && expression() == other.expression() && + exception() == other.exception() && message() == other.message(); + } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"expects-exception", _expects_exception}}; } + + /** + * Returns a new `assert` statement with the expression replaced. + * + * @param e original statement + * @param c new expresssion + * @return new statement that's equal to original one but with the expression replaced + */ + static Statement setCondition(const Assert& e, const hilti::Expression& c) { + auto x = Statement(e)._clone().as(); + x.childs()[0] = c; + return x; + } + +private: + bool _expects_exception = false; +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/block.h b/hilti/include/ast/statements/block.h new file mode 100644 index 000000000..835820dc0 --- /dev/null +++ b/hilti/include/ast/statements/block.h @@ -0,0 +1,37 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a statement block. */ +class Block : public NodeBase, public hilti::trait::isStatement { +public: + Block(std::vector stmts = {}, Meta m = Meta()) : NodeBase(nodes(std::move(stmts)), std::move(m)) {} + + auto statements() const { return childs(0, -1); } + + bool operator==(const Block& /* other */) const { + // return statements() == other.statements(); + return true; // FIXME + } + + /** Internal method for use by builder API only. */ + void _add(Statement s) { addChild(std::move(s)); } + + /** Internal method for use by builder API only. */ + auto& _lastStatementNode() { return childs().back(); } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/break.h b/hilti/include/ast/statements/break.h new file mode 100644 index 000000000..65ab4cccd --- /dev/null +++ b/hilti/include/ast/statements/break.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a ``break`` statement. */ +class Break : public NodeBase, public hilti::trait::isStatement { +public: + Break(Meta m = Meta()) : NodeBase({}, std::move(m)) {} + + bool operator==(const Break& /* other */) const { return true; } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/comment.h b/hilti/include/ast/statements/comment.h new file mode 100644 index 000000000..5850cda10 --- /dev/null +++ b/hilti/include/ast/statements/comment.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti { +namespace statement { + +namespace comment { +enum class Separator { After, BeforeAndAfter, Before }; +} // namespace comment + +/** AST node for an comment that will be passed through code generation.. */ +class Comment : public NodeBase, public hilti::trait::isStatement { +public: + Comment(std::string comment, comment::Separator separator = comment::Separator::Before, + const Meta& /* m */ = Meta()) + : _comment(std::move(comment)), _separator(separator) {} + + auto comment() const { return _comment; } + auto separator() const { return _separator; } + + bool operator==(const Comment& other) const { return comment() == other.comment(); } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"comment", _comment}}; } + +private: + std::string _comment; + comment::Separator _separator; +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/continue.h b/hilti/include/ast/statements/continue.h new file mode 100644 index 000000000..6cf00714b --- /dev/null +++ b/hilti/include/ast/statements/continue.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a ``continue`` statement. */ +class Continue : public NodeBase, public hilti::trait::isStatement { +public: + Continue(Meta m = Meta()) : NodeBase({}, std::move(m)) {} + + bool operator==(const Continue& /* other */) const { return true; } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/declaration.h b/hilti/include/ast/statements/declaration.h new file mode 100644 index 000000000..faca30419 --- /dev/null +++ b/hilti/include/ast/statements/declaration.h @@ -0,0 +1,28 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a statement representing a declaration.. */ +class Declaration : public NodeBase, public hilti::trait::isStatement { +public: + Declaration(hilti::Declaration d, Meta m = Meta()) : NodeBase({std::move(d)}, std::move(m)) {} + + auto declaration() const { return child<::hilti::Declaration>(0); } + + bool operator==(const Declaration& other) const { return declaration() == other.declaration(); } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/expression.h b/hilti/include/ast/statements/expression.h new file mode 100644 index 000000000..2e2f4f484 --- /dev/null +++ b/hilti/include/ast/statements/expression.h @@ -0,0 +1,28 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for an expression statement. */ +class Expression : public NodeBase, public hilti::trait::isStatement { +public: + Expression(hilti::Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + + auto expression() const { return child<::hilti::Expression>(0); } + + bool operator==(const Expression& other) const { return expression() == other.expression(); } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/for.h b/hilti/include/ast/statements/for.h new file mode 100644 index 000000000..a6ff7831c --- /dev/null +++ b/hilti/include/ast/statements/for.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a "while" statement. */ +class For : public NodeBase, public hilti::trait::isStatement { +public: + For(hilti::ID id, hilti::Expression seq, Statement body, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(seq), std::move(body)), std::move(m)) {} + + auto id() const { return child(0); } + auto sequence() const { return child(1); } + auto body() const { return child(2); } + + /** + * Returns the body's scope. Note that the scope is shared among any + * copies of an instance. + */ + std::shared_ptr scope() const { return childs()[2].scope(); } + + bool operator==(const For& other) const { + return id() == other.id() && sequence() == other.sequence() && body() == other.body(); + } + + /** Internal method for use by builder API only. */ + auto& _sequenceNode() { return childs()[1]; } + + /** Internal method for use by builder API only. */ + auto& _bodyNode() { return childs()[2]; } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/if.h b/hilti/include/ast/statements/if.h new file mode 100644 index 000000000..7a6ac3b77 --- /dev/null +++ b/hilti/include/ast/statements/if.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a "if" statement. */ +class If : public NodeBase, public hilti::trait::isStatement { +public: + If(const hilti::Declaration& init, std::optional cond, Statement true_, + std::optional false_, Meta m = Meta()) + : NodeBase(nodes(init, std::move(cond), std::move(true_), std::move(false_)), std::move(m)) { + if ( ! init.isA() ) + logger().internalError("initialization for 'if' must be a local declaration"); + } + + If(hilti::Expression cond, Statement true_, std::optional false_, Meta m = Meta()) + : NodeBase(nodes(node::none, std::move(cond), std::move(true_), std::move(false_)), std::move(m)) {} + + auto init() const { return childs()[0].tryAs(); } + auto condition() const { return childs()[1].tryAs(); } + auto true_() const { return child(2); } + auto false_() const { return childs()[3].tryAs(); } + + bool operator==(const If& other) const { + return init() == other.init() && condition() == other.condition() && true_() == other.true_() && + false_() == other.false_(); + } + + /** Internal method for use by builder API only. */ + auto& _trueNode() { return childs()[2]; } + + /** Internal method for use by builder API only. */ + auto& _falseNode() { return childs()[3]; } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new "if" statement with the init expression replaced. + * + * @param e original statement + * @param d new init expresssion + * @return new statement that's equal to original one but with the init expression replaced + */ + static Statement setInit(const If& e, const hilti::Declaration& d) { + auto x = Statement(e)._clone().as(); + x.childs()[0] = d; + return x; + } + + /** + * Returns a new "if" statement with the condition expression replaced. + * + * @param e original statement + * @param c new condition expresssion + * @return new statement that's equal to original one but with the condition replaced + */ + static Statement setCondition(const If& e, const hilti::Expression& c) { + auto x = Statement(e)._clone().as(); + x.childs()[1] = c; + return x; + } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/return.h b/hilti/include/ast/statements/return.h new file mode 100644 index 000000000..0358a0e08 --- /dev/null +++ b/hilti/include/ast/statements/return.h @@ -0,0 +1,47 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a "return" statement. */ +class Return : public NodeBase, public hilti::trait::isStatement { +public: + Return(Meta m = Meta()) : NodeBase({}, std::move(m)) {} + Return(hilti::Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + + std::optional expression() const { + if ( ! childs().empty() ) + return child<::hilti::Expression>(0); + + return {}; + } + + bool operator==(const Return& other) const { return expression() == other.expression(); } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new "return" statement with the expression replaced. + * + * @param e original statement + * @param c new expresssion + * @return new statement that's equal to original one but with the expression replaced + */ + static Statement setExpression(const Return& e, const hilti::Expression& c) { + auto x = Statement(e)._clone().as(); + x.childs()[0] = c; + return x; + } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/switch.h b/hilti/include/ast/statements/switch.h new file mode 100644 index 000000000..3b41ac1ba --- /dev/null +++ b/hilti/include/ast/statements/switch.h @@ -0,0 +1,167 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace hilti { +namespace statement { + +class Switch; + +namespace switch_ { + +using Default = struct {}; + +/** + * AST node for a switch case type. + * + * Note that internally, we store the expressions in a preprocessed matter: + * `E` turns into ` == E`, where ID is selected to match the code + * generator's output. Doing this allows coercion for the comparision to + * proceed normally. The preprocessing happens at the time the `Case` gets + * added to a `Switch` statement, and the new versions are stored separately + * from the original expressions. + */ +class Case : public NodeBase { +public: + Case(hilti::Expression expr, Statement body, Meta m = Meta()) + : NodeBase(nodes(std::move(body), std::move(expr)), std::move(m)), _end_exprs(2) {} + Case(std::vector exprs, Statement body, Meta m = Meta()) + : NodeBase(nodes(std::move(body), std::move(exprs)), std::move(m)), _end_exprs(childs().size()) {} + Case(Default /*unused*/, Statement body, Meta m = Meta()) + : NodeBase(nodes(std::move(body)), std::move(m)), _end_exprs(1) {} + Case() = default; + + auto expressions() const { return childs(1, _end_exprs); } + auto preprocessedExpressions() const { return childs(_end_exprs, -1); } + auto body() const { return child(0); } + + bool isDefault() const { return expressions().empty(); } + + /** Internal method for use by builder API only. */ + auto& _bodyNode() { return childs()[0]; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Case& other) const { return expressions() == other.expressions() && body() == other.body(); } + + /** + * Replaces a case's expresssions. + * + * @param c case to replace expressions in + * @param exprs new expressions + * @return new case that is a duplicate of *c* but has its expressions replaced with *expr* + */ + static Case setExpressions(Case c, std::vector exprs) { + c.childs() = hilti::nodes(c._bodyNode(), std::move(exprs)); + return c; + } + +private: + friend class hilti::statement::Switch; + + void _addPreprocessedExpression(hilti::Expression e) { childs().emplace_back(std::move(e)); } + + int _end_exprs{}; +}; + +inline Node to_node(Case c) { return Node(std::move(c)); } + +} // namespace switch_ + +/** AST node for a "switch" statement. */ +class Switch : public NodeBase, public hilti::trait::isStatement { +public: + Switch(hilti::Expression cond, const std::vector& cases, Meta m = Meta()) + : NodeBase(nodes(node::none, std::move(cond), cases), std::move(m)) { + _preprocessCases("__x"); + } + + Switch(const hilti::Declaration& init, hilti::Expression cond, const std::vector& cases, + Meta m = Meta()) + : NodeBase(nodes(init, std::move(cond), cases), std::move(m)) { + if ( ! init.isA() ) + logger().internalError("initialization for 'switch' must be a local declaration"); + _preprocessCases(init.id()); + } + + auto init() const { return childs()[0].tryAs(); } + auto expression() const { return childs()[1].as(); } + auto type() const { + if ( auto i = init() ) + return i->as().type(); + + return expression().type(); + } + + auto cases() const { + std::vector cases; + for ( auto& c : childs(2, -1) ) { + if ( ! c.isDefault() ) + cases.push_back(c); + } + + return cases; + } + + auto caseNodes() { return nodesOfType(); } + + std::optional default_() const { + for ( auto& c : childs(2, -1) ) { + if ( c.isDefault() ) + return c; + } + return {}; + } + + bool operator==(const Switch& other) const { + return init() == other.init() && expression() == other.expression() && default_() == other.default_() && + cases() == other.cases(); + } + + /** Internal method for use by builder API only. */ + auto& _lastCaseNode() { return childs().back(); } + + /** Internal method for use by builder API only. */ + void _addCase(switch_::Case case_) { + for ( const auto& c : case_.expressions() ) + case_._addPreprocessedExpression(expression::UnresolvedOperator(operator_::Kind::Equal, + {expression::UnresolvedID(ID(_id)), c}, + c.meta())); + + addChild(std::move(case_)); + } + + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + void _preprocessCases(const std::string& id) { + _id = id; + + for ( uint64_t i = 2; i < childs().size(); i++ ) { + auto& case_ = childs()[i].as(); + for ( const auto& c : case_.expressions() ) + case_._addPreprocessedExpression(expression::UnresolvedOperator(operator_::Kind::Equal, + {expression::UnresolvedID(ID(id)), c}, + c.meta())); + } + } + + std::string _id; +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/throw.h b/hilti/include/ast/statements/throw.h new file mode 100644 index 000000000..c19393759 --- /dev/null +++ b/hilti/include/ast/statements/throw.h @@ -0,0 +1,29 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a "throw" statement. */ +class Throw : public NodeBase, public hilti::trait::isStatement { +public: + Throw(Meta m = Meta()) : NodeBase({node::none}, std::move(m)) {} + Throw(hilti::Expression excpt, Meta m = Meta()) : NodeBase({std::move(excpt)}, std::move(m)) {} + + auto expression() const { return childs()[0].tryAs(); } + + bool operator==(const Throw& other) const { return expression() == other.expression(); } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/try.h b/hilti/include/ast/statements/try.h new file mode 100644 index 000000000..0a5741173 --- /dev/null +++ b/hilti/include/ast/statements/try.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace statement { + +namespace try_ { + +/** + * AST node for a `catch` block. + */ +class Catch : public NodeBase { +public: + Catch(hilti::Statement body, Meta m = Meta()) : NodeBase(nodes(node::none, std::move(body)), std::move(m)) {} + Catch(const hilti::Declaration& param, Statement body, Meta m = Meta()) + : NodeBase(nodes(param, std::move(body)), std::move(m)) { + if ( ! param.isA() ) + logger().internalError("'catch' hilti::Declaration must be parameter"); + } + Catch() = default; + + std::optional parameter() const { + auto d = childs()[0].tryAs(); + if ( d ) + return d->as(); + + return {}; + } + + auto body() const { return child(1); } + + /** Internal method for use by builder API only. */ + auto& _bodyNode() { return childs()[1]; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Catch& other) const { return parameter() == other.parameter() && body() == other.body(); } +}; + +} // namespace try_ + +/** AST node for a "try" statement. */ +class Try : public NodeBase, public hilti::trait::isStatement { +public: + Try(hilti::Statement body, std::vector catches, Meta m = Meta()) + : NodeBase(nodes(std::move(body), std::move(catches)), std::move(m)) {} + + auto body() const { return child(0); } + auto catches() const { return childs(1, -1); } + + bool operator==(const Try& other) const { return body() == other.body() && catches() == other.catches(); } + + /** Internal method for use by builder API only. */ + auto& _bodyNode() { return childs()[0]; } + + /** Internal method for use by builder API only. */ + auto& _lastCatchNode() { return childs().back(); } + + /** Internal method for use by builder API only. */ + void _addCatch(try_::Catch catch_) { addChild(std::move(catch_)); } + + /** Implements the `Statement` interface. */ + auto isEqual(const hilti::Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/while.h b/hilti/include/ast/statements/while.h new file mode 100644 index 000000000..7b28f4a75 --- /dev/null +++ b/hilti/include/ast/statements/while.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a "while" statement. */ +class While : public NodeBase, public hilti::trait::isStatement { +public: + While(const hilti::Declaration& init, std::optional cond, Statement body, + std::optional else_ = {}, Meta m = Meta()) + : NodeBase(nodes(init, std::move(cond), std::move(body), std::move(else_)), std::move(m)) { + if ( ! init.isA() ) + logger().internalError("initialization for 'while' must be a local declaration"); + } + + While(hilti::Expression cond, Statement body, Meta m = Meta()) + : NodeBase(nodes(node::none, std::move(cond), std::move(body), node::none), std::move(m)) {} + + While(hilti::Expression cond, Statement body, std::optional else_, Meta m = Meta()) + : NodeBase(nodes(node::none, std::move(cond), std::move(body), std::move(else_)), std::move(m)) {} + + auto init() const { return childs()[0].tryAs(); } + auto condition() const { return childs()[1].tryAs(); } + auto body() const { return child(2); } + auto else_() const { return childs()[3].tryAs(); } + + bool operator==(const While& other) const { + return init() == other.init() && condition() == other.condition() && body() == other.body() && + else_() == other.else_(); + } + + /** Internal method for use by builder API only. */ + auto& _bodyNode() { return childs()[2]; } + + /** Internal method for use by builder API only. */ + auto& _elseNode() { return childs()[3]; } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Returns a new "while" statement with the init expression replaced. + * + * @param e original statement + * @param d new init expresssion + * @return new statement that's equal to original one but with the init expression replaced + */ + static Statement setInit(const While& e, const hilti::Declaration& d) { + auto x = Statement(e)._clone().as(); + x.childs()[0] = d; + return x; + } + + /** + * Returns a new "while" statement with the condition expression replaced. + * + * @param d original statement + * @param c new condition expresssion + * @return new statement that's equal to original one but with the condition replaced + */ + static Statement setCondition(const While& e, const hilti::Expression& c) { + auto x = Statement(e)._clone().as(); + x.childs()[1] = c; + return x; + } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/statements/yield.h b/hilti/include/ast/statements/yield.h new file mode 100644 index 000000000..b3fa58aa5 --- /dev/null +++ b/hilti/include/ast/statements/yield.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace statement { + +/** AST node for a "yield" statement. */ +class Yield : public NodeBase, public hilti::trait::isStatement { +public: + Yield(Meta m = Meta()) : NodeBase({}, std::move(m)) {} + + bool operator==(const Yield& /* other */) const { return true; } + + /** Implements the `Statement` interface. */ + auto isEqual(const Statement& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace statement +} // namespace hilti diff --git a/hilti/include/ast/type.api b/hilti/include/ast/type.api new file mode 100644 index 000000000..90ef06905 --- /dev/null +++ b/hilti/include/ast/type.api @@ -0,0 +1,120 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** Interface for HILTI types. */ +class Type(hilti::trait::isType) : hilti::trait::isNode { + /** Returns true if the type is equivalent to another HILTI type. */ + bool isEqual(const Type& other) const; + + /** + * Returns any parameters associated with type. If a type is declared as + * `T` this returns a vector of the AST nodes for `A`, `B`, and + * `C`. + */ + std::vector typeParameters() const if hilti::type::trait::isParameterized else {}; + + /** + * Returns true if all instances of the same type class can be coerced + * into the current instance, independent of their pararameters. In HILTI + * source code, this typically corresponds to a type `T<*>`. + */ + bool isWildcard() const if hilti::type::trait::isParameterized else false; + + /** Returns the set of flags associated with the type. */ + type::Flags flags() const; + + /** Returns true if a given type flag has been set. */ + bool hasFlag(type::Flag f) const; + + /** Returns the type of an iterator for this type. */ + Type iteratorType(bool const_) const if hilti::type::trait::isIterable or hilti::type::trait::isView; + + /** Returns the type of an view for this type. */ + Type viewType() const if hilti::type::trait::isViewable; + + /** Returns the type of elements the iterator traveres. */ + Type dereferencedType() const if hilti::type::trait::isDereferencable; + + /** Returns the type of elements the container stores. */ + Type elementType() const if hilti::type::trait::isIterable; + + /** + * Returns the fully deferenced type that this type corresponds to. This, + * for example, follows a type ID to the actual type it maps to. + */ + Type effectiveType() const if hilti::type::trait::hasDynamicType; + + /** For internal use. Use ``type::isAllocable` instead. */ + trait _isAllocable() from hilti::type::trait::isAllocable; + + /** For internal use. Use ``type::isDereferencable` instead. */ + trait _isDereferencable() from hilti::type::trait::isDereferencable; + + /** For internal use. Use ``type::isIterable` instead. */ + trait _isIterable() from hilti::type::trait::isIterable; + + /** For internal use. Use ``type::isViewable` instead. */ + trait _isViewable() from hilti::type::trait::isViewable; + + /** For internal use. Use ``type::isIterator` instead. */ + trait _isIterator() from hilti::type::trait::isIterator; + + /** For internal use. Use ``type::isView` instead. */ + trait _isView() from hilti::type::trait::isView; + + /** For internal use. Use ``type::isParameterized` instead. */ + trait _isParameterized() from hilti::type::trait::isParameterized; + + /** For internal use. Use ``type::isReferenceType` instead. */ + trait _isReferenceType() from hilti::type::trait::isReferenceType; + + /** For internal use. Use ``type::isMutable` instead. */ + trait _isMutable() from hilti::type::trait::isMutable; + + /** For internal use. Use ``type::hasDynamicInstead` instead. */ + trait _hasDynamicType() from hilti::type::trait::hasDynamicType; + + /** For internal use. Use ``type::isRuntimeNonTrivial` instead. */ + trait _isRuntimeNonTrivial() from hilti::type::trait::isRuntimeNonTrivial; + + /** For internal use. Use ``type::isOnHeap` instead. */ + trait _isOnHeap() from hilti::type::trait::isOnHeap; + + /** For internal use. Use ``type::isConstant()` instead. */ + bool _isConstant() const; + + /** For internal use. */ + type::detail::State& _state(); + + /** + * Returns a HILTI ID associated with the type if any has been set. This + * method is implemented for all types in `TypeBase`. + */ + std::optional typeID() const; + + /** + * Returns a C++ ID associated with the type if any has been set. This + * method is implemented for all types in `TypeBase`. + */ + std::optional cxxID() const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + + /** Implements the `Node` interface. */ + const std::vector& childs() const; + + /** Implements the `Node` interface. */ + std::vector& childs(); + + /** Implements the `Node` interface. */ + const Meta& meta() const; + + /** Implements the `Node` interface. */ + void setMeta(Meta m); + + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/hilti/include/ast/type.h b/hilti/include/ast/type.h new file mode 100644 index 000000000..395515451 --- /dev/null +++ b/hilti/include/ast/type.h @@ -0,0 +1,364 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include + +namespace hilti { + +namespace trait { +/** Trait for classes implementing the `Type` interface. */ +class isType : public isNode {}; +} // namespace trait + +namespace type { + +namespace trait { +class hasDynamicType {}; +class isAllocable {}; +class isDereferencable {}; +class isIterable {}; +class isIterator {}; +class isMutable {}; +class isOnHeap {}; +class isParameterized {}; +class isReferenceType {}; +class isRuntimeNonTrivial {}; +class isView {}; +class isViewable {}; +class supportsWildcard {}; +} // namespace trait + +/** Additional flags to associated with types. */ +enum class Flag { + /** Set to make the type `const`. */ + Constant = (1U << 0U), + + /** + * Marks the type as having a top-level scope that does not derive scope content + * from other nodes above it in the AST (except for truely global IDs). + */ + NoInheritScope = (1U << 1U), +}; + +/** + * Stores a set of flags associated with a type. + * + * TODO: Replace with 3rd-party/enum-class/EnumClass.h + */ +class Flags { +public: + Flags() = default; + Flags(Flag f) : _flags(static_cast(f)) {} + Flags(const Flags&) = default; + Flags(Flags&&) noexcept = default; + ~Flags() = default; + + /** Returns true if a given flag has been set. */ + bool has(Flag f) const { return _flags & static_cast(f); } + + /** Sets (or clear) a given flag. */ + void set(type::Flag flag, bool set = true) { + if ( set ) + _flags |= static_cast(flag); + else + _flags &= ~static_cast(flag); + } + + Flags operator+(Flag f) { + auto x = Flags(*this); + x.set(f); + return x; + } + + Flags operator+(const Flags& other) const { + auto x = Flags(); + x._flags = _flags | other._flags; + return x; + } + + Flags& operator+=(Flag f) { + set(f); + return *this; + } + Flags& operator+=(const Flags& other) { + _flags |= other._flags; + return *this; + } + + Flags operator-(const Flags& other) const { + auto x = Flags(); + x._flags = _flags & ~other._flags; + return x; + } + + Flags& operator-=(Flag f) { + set(f, false); + return *this; + } + Flags& operator-=(const Flags& other) { + _flags &= ~other._flags; + return *this; + } + + Flags& operator=(Flag f) { + set(f); + return *this; + } + Flags& operator=(const Flags&) = default; + Flags& operator=(Flags&&) noexcept = default; + + bool operator==(Flags other) const { return _flags == other._flags; } + + bool operator!=(Flags other) const { return _flags != other._flags; } + +private: + uint64_t _flags = 0; +}; + +inline Flags operator+(Flag f1, Flag f2) { return Flags(f1) + f2; } + +namespace detail { + +struct State { + std::optional id; + std::optional cxx; + type::Flags flags; +}; + +#include + +/** Creates an AST node representing a `Type`. */ +inline Node to_node(Type t) { return Node(std::move(t)); } + +/** Renders a type as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, Type t) { return out << to_node(std::move(t)); } + +} // namespace detail +} // namespace type + +using Type = type::detail::Type; + +/** + * Base class for classes implementing the `Type` interface. This class + * provides implementations for some interface methods shared that are shared + * by all types. + */ +class TypeBase : public NodeBase, public hilti::trait::isType { +public: + using NodeBase::NodeBase; + + /** Implements the `Type` interface. */ + bool hasFlag(type::Flag f) const { return _state().flags.has(f); } + /** Implements the `Type` interface. */ + type::Flags flags() const { return _state().flags; } + /** Implements the `Type` interface. */ + bool _isConstant() const { return _state().flags.has(type::Flag::Constant); } + /** Implements the `Type` interface. */ + std::optional typeID() const { return _state().id; } + /** Implements the `Type` interface. */ + std::optional cxxID() const { return _state().cxx; } + /** Implements the `Type` interface. */ + const type::detail::State& _state() const { return _state_; } + /** Implements the `Type` interface. */ + type::detail::State& _state() { return _state_; } + +private: + type::detail::State _state_; +}; + +namespace type { + +/** + * Copies an existing type, adding additional type flags. + * + * @param t original type + * @param flags additional flags + * @return new type with the additional flags set + */ +inline hilti::Type addFlags(const Type& t, const type::Flags& flags) { + auto x = t._clone(); + x._state().flags += flags; + return x; +} + +/** + * Copies an existing type, removing specified type flags. + * + * @param t original type + * @param flags flags to remove + * @return new type with the flags removed + */ +inline hilti::Type removeFlags(const Type& t, const type::Flags& flags) { + auto x = t._clone(); + x._state().flags -= flags; + return x; +} + +/** + * Copies an existing type, marking the type as one that stores a + * constant/non-constant value. This has only an effect for mutable types. + * Non-mutable are always considered const. The default for for mutable * + * types is non-const. + * + * @param t original type + * @param const_ boolen indicating whether the new type should be const + * @return new type with the constness changed as requested + */ +inline hilti::Type setConstant(const Type& t, bool const_) { + auto x = t._clone(); + x._state().flags.set(type::Flag::Constant, const_); + return x; +} + +/** + * Copies an existing type, setting its C++ ID as emitted by the code generator. + * + * @param t original type + * @param id new C++ ID + * @return new type with the C++ ID set accordindly + */ +inline hilti::Type setCxxID(const Type& t, ID id) { + auto x = t._clone(); + x._state().cxx = std::move(id); + return x; +} + +/** + * Copies an existing type, setting its asssociated type ID. + * + * @param t original type + * @param id new type ID + * @return new type with associateed type ID set accordindly + */ +inline hilti::Type setTypeID(const Type& t, ID id) { + auto x = t._clone(); + x._state().id = std::move(id); + return x; +} + +/** + * Place-holder class used to enable overloading of type constructors when + * creating wildcard types. + */ +class Wildcard {}; + +/** + * Fully deferences a type, returning the type it ultimately refers to. For + * most types, this will return them directly, but types with + * `trait::hasDynamicProcess` can customize the process (e.g., a resolved + * type ID will return the type the ID refers to. ) + */ +inline Type effectiveType(Type t) { return t._hasDynamicType() ? t.effectiveType() : std::move(t); } + +/** + * Like `effectiveType`, accepts an optional type. If not set, the returned + * type will likely remain unset. + */ +inline std::optional effectiveOptionalType(std::optional t) { + if ( t ) + return effectiveType(*t); + + return {}; +} + +/** Returns true for HILTI types that can be used to instantiate variables. */ +inline bool isAllocable(const Type& t) { return effectiveType(t)._isAllocable(); } + +/** Returns true for HILTI types that one can iterator over. */ +inline bool isDereferencable(const Type& t) { return effectiveType(t)._isDereferencable(); } + +/** Returns true for HILTI types that one can iterator over. */ +inline bool isIterable(const Type& t) { return effectiveType(t)._isIterable(); } + +/** Returns true for HILTI types that represent iterators. */ +inline bool isIterator(const Type& t) { return effectiveType(t)._isIterator(); } + +/** Returns true for HILTI types that are parameterized with a set of type parameters. */ +inline bool isParameterized(const Type& t) { return effectiveType(t)._isParameterized(); } + +/** Returns true for HILTI types that implement a reference to another type. */ +inline bool isReferenceType(const Type& t) { return effectiveType(t)._isReferenceType(); } + +/** Returns true for HILTI types that can change their value. */ +inline bool isMutable(const Type& t) { return effectiveType(t)._isMutable(); } + +/** Returns true for HILTI types that, when compiled, correspond to non-POD C++ types. */ +inline bool isRuntimeNonTrivial(const Type& t) { return effectiveType(t)._isRuntimeNonTrivial(); } + +/** Returns true for HILTI types that represent iterators. */ +inline bool isView(const Type& t) { return effectiveType(t)._isView(); } + +/** Returns true for HILTI types that one can create a view for. */ +inline bool isViewable(const Type& t) { return effectiveType(t)._isViewable(); } + +/** Returns true for HILTI types that are always to be placed on the heap. */ +inline bool isOnHeap(const Type& t) { return effectiveType(t)._isOnHeap(); } + +/** Returns true if the type is a constant. */ +inline bool isConstant(const Type& t) { + auto et = effectiveType(t); + + return et.flags().has(type::Flag::Constant); +} + +/** Returns a `const` version of a type. */ +inline auto constant(const Type& t) { return setConstant(t, true); } + +/** Returns a not `const` version of a type. */ +inline auto nonConstant(const Type& t) { return setConstant(t, false); } + +/** Sets the constness of type *t* to that of another type *from*. */ +inline auto transferConstness(const Type& t, const Type& from) { return setConstant(t, isConstant(from)); } + +namespace detail { +inline bool operator==(const Type& t1, const Type& t2) { + if ( &t1 == &t2 ) + return true; + + if ( type::isConstant(t1) != type::isConstant(t2) ) + return false; + + if ( t1.cxxID() && t2.cxxID() ) + return t1.cxxID() == t2.cxxID(); + + if ( (t1.flags() - type::Flag::Constant) != (t2.flags() - type::Flag::Constant) ) + return false; + + // Type comparision is not fully symmetric, it's good enough + // if one type believes it matches the other one. + return t1.isEqual(t2) || t2.isEqual(t1); +} + +inline bool operator!=(const Type& t1, const Type& t2) { return ! (t1 == t2); } + +} // namespace detail + +/** + * Checks if a source type's constness suports promotion to a destination's + * constness. This ignores the types itself, it just looks at constness. + */ +inline bool isConstCompatible(const Type& src, const Type& dst) { + if ( type::isConstant(dst) ) + return true; + + return ! type::isConstant(src); +} + +/** Returns true if two types are identical, ignoring for their constnesses. */ +inline bool sameExceptForConstness(const Type& t1, const Type& t2) { return t1.isEqual(t2) || t2.isEqual(t1); } + +} // namespace type + +/** Constructs an AST node from any class implementing the `Type` interface. */ +template::value>* = nullptr> +inline Node to_node(T t) { + return Node(Type(std::move(t))); +} + +} // namespace hilti diff --git a/hilti/include/ast/types/address.h b/hilti/include/ast/types/address.h new file mode 100644 index 000000000..ed3c9c5f5 --- /dev/null +++ b/hilti/include/ast/types/address.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a address type. */ +class Address : public TypeBase, trait::isAllocable { +public: + Address(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Address& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/all.h b/hilti/include/ast/types/all.h new file mode 100644 index 000000000..a52999648 --- /dev/null +++ b/hilti/include/ast/types/all.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/ast/types/any.h b/hilti/include/ast/types/any.h new file mode 100644 index 000000000..cb87ceeb3 --- /dev/null +++ b/hilti/include/ast/types/any.h @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for an "any" type. */ +class Any : public TypeBase { +public: + Any(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Any& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/bool.h b/hilti/include/ast/types/bool.h new file mode 100644 index 000000000..8cb57959c --- /dev/null +++ b/hilti/include/ast/types/bool.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a bool type. */ +class Bool : public TypeBase, trait::isAllocable { +public: + Bool(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Bool& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/bytes.h b/hilti/include/ast/types/bytes.h new file mode 100644 index 000000000..8f991d71c --- /dev/null +++ b/hilti/include/ast/types/bytes.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +namespace bytes { + +/** AST node for a list iterator type. */ +class Iterator : public TypeBase, + trait::isIterator, + trait::isDereferencable, + trait::isAllocable, + trait::isMutable, + trait::isRuntimeNonTrivial { +public: + Iterator(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Iterator& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type dereferencedType() const; + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace bytes + +/** AST node for a bytes type. */ +class Bytes : public TypeBase, trait::isAllocable, trait::isMutable, trait::isIterable, trait::isRuntimeNonTrivial { +public: + Bytes(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Bytes& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return type::UnsignedInteger(8); } + + /** Implements the `Type` interface. */ + Type iteratorType(bool /* const_ */) const { return bytes::Iterator(meta()); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + std::optional _etype; +}; + +namespace detail::bytes { +inline Node element_type = Node(type::UnsignedInteger(8, Location())); +} // namespace detail::bytes + +inline Type bytes::Iterator::dereferencedType() const { return type::UnsignedInteger(8); } + +} // namespace type + +} // namespace hilti diff --git a/hilti/include/ast/types/computed.h b/hilti/include/ast/types/computed.h new file mode 100644 index 000000000..bbe364269 --- /dev/null +++ b/hilti/include/ast/types/computed.h @@ -0,0 +1,99 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace hilti { +namespace type { + +/** + * AST node for a type computed dynamically from another node that's + * potentially not resolved at first yet. This works either through a + * callback that executes at time the type is accessed, with access to the + * original node; or through an expression which's type at the time of access + * determined the result. + * + * @note This class gets a full set of traits, so that it can forward all + * method calls to the resulting type. + */ +class Computed : public TypeBase, + trait::hasDynamicType, + type::trait::isParameterized, + type::trait::isViewable, + trait::isDereferencable, + trait::isIterable { +public: + using Callback = std::function; + Computed(NodeRef r, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _node(std::move(std::move(r))) {} + Computed(NodeRef r, Callback cb, Meta m = Meta()) + : TypeBase(nodes(node::none), std::move(m)), _node(std::move(std::move(r))), _callback(std::move(cb)) {} + Computed(Expression e, Meta m = Meta()) : TypeBase(nodes(std::move(e)), std::move(m)) {} + Computed(Expression e, bool change_constness_to, Meta m = Meta()) + : TypeBase(nodes(std::move(e)), std::move(m)), _change_constness_to(change_constness_to) {} + Computed(Type t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} + Computed(Type t, Callback cb, Meta m = Meta()) + : TypeBase(nodes(std::move(t)), std::move(m)), _callback(std::move(cb)) {} + + Type type() const { + if ( _node ) { + if ( _callback ) + return type::effectiveType(_callback(*_node)); + else + return type::effectiveType(_node->template as()); + } + + if ( auto e = childs()[0].tryAs() ) { + if ( ! _change_constness_to.has_value() ) + return e->type(); + + if ( *_change_constness_to ) + return type::constant(e->type()); + + return type::nonConstant(e->type()); + } + + if ( auto t = childs()[0].tryAs() ) { + if ( _callback ) + return type::effectiveType(_callback(const_cast(childs()[0]))); + else + return *t; + } + + return type::unknown; + } + + bool operator==(const Computed& other) const { return type() == other.type(); } + + /** Implements the `Type` interface. */ + bool isEqual(const Type& other) const { return type() == other; } + /** Implements the `Type` interface. */ + Type effectiveType() const { return type::effectiveType(type()); } + + std::vector typeParameters() const { return type().typeParameters(); } + bool isWildcard() const { return type().isWildcard(); } + Type iteratorType(bool const_) const { return type().iteratorType(const_); } + Type viewType() const { return type().viewType(); } + Type dereferencedType() const { return type().dereferencedType(); } + Type elementType() const { return type().elementType(); } + + /** Implements the `Node` interface. */ + auto properties() const { + return _node ? node::Properties{{"resolved", _node.renderedRid()}} : node::Properties{{}}; + } + +private: + NodeRef _node; + Callback _callback; + std::optional _change_constness_to; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/doc-only.h b/hilti/include/ast/types/doc-only.h new file mode 100644 index 000000000..23beddfd4 --- /dev/null +++ b/hilti/include/ast/types/doc-only.h @@ -0,0 +1,37 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace hilti { +namespace type { + +/** + * AST node for a type that's only used for documentation purposes. This type + * allows to carry a textual description of the a type over into + * auto-generated documentation. If it's used anywhere else, it'll cause + * trouble. + */ +class DocOnly : public TypeBase { +public: + DocOnly(std::string desc, Meta m = Meta()) : TypeBase(std::move(m)), _description(std::move(desc)) {} + + auto description() const { return _description; } + + bool operator==(const DocOnly& /* other */) const { return false; } + + // Type interface. + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + std::string _description; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/enum.h b/hilti/include/ast/types/enum.h new file mode 100644 index 000000000..7475be5de --- /dev/null +++ b/hilti/include/ast/types/enum.h @@ -0,0 +1,95 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +namespace enum_ { +/** AST node for an enum label. */ +class Label : public NodeBase { +public: + Label() : NodeBase({ID("")}, Meta()) {} + Label(ID id, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)) {} + Label(ID id, int v, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)), _value(v) {} + + auto id() const { return child(0); } + auto value() const { return _value; } + + bool operator==(const Label& other) const { return id() == other.id() && value() == other.value(); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"value", _value}}; } + +private: + int _value = -1; +}; + +inline Node to_node(Label l) { return Node(std::move(l)); } + +} // namespace enum_ + +/** AST node for an enum type. */ +class Enum : public TypeBase, trait::isAllocable, trait::isParameterized { +public: + Enum(std::vector l, Meta m = Meta()) + : TypeBase(nodes(_normalizeLabels(std::move(l))), std::move(m)) {} + Enum(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} + + std::vector labels() const { return childs(0, -1); } + + /** + * Returns the set of labels but makes sure to include each enumator + * value at most once. + */ + std::vector uniqueLabels() const { + auto pred_gt = [](const enum_::Label& e1, const enum_::Label& e2) { return e1.value() > e2.value(); }; + auto pred_eq = [](const enum_::Label& e1, const enum_::Label& e2) { return e1.value() == e2.value(); }; + std::vector x = labels(); + std::sort(x.begin(), x.end(), pred_gt); + x.erase(std::unique(x.begin(), x.end(), pred_eq), x.end()); + return x; + } + + std::optional label(const ID& id) const { + for ( auto l : labels() ) { + if ( l.id() == id ) + return l; + } + + return {}; + } + + bool operator==(const Enum& other) const { + if ( typeID() && other.typeID() ) + return *typeID() == *other.typeID(); + + return labels() == other.labels(); + } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { + std::vector params; + for ( auto&& c : uniqueLabels() ) + params.emplace_back(std::move(c)); + return params; + } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + static std::vector _normalizeLabels(std::vector /*labels*/); + + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/error.h b/hilti/include/ast/types/error.h new file mode 100644 index 000000000..f64d6d1e9 --- /dev/null +++ b/hilti/include/ast/types/error.h @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for an error type. */ +class Error : public TypeBase, trait::isAllocable { +public: + Error(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Error& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/exception.h b/hilti/include/ast/types/exception.h new file mode 100644 index 000000000..1591341dd --- /dev/null +++ b/hilti/include/ast/types/exception.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for an `exception` type. */ +class Exception : public TypeBase, trait::isAllocable, trait::isParameterized { +public: + Exception(Meta m = Meta()) : TypeBase({node::none}, std::move(m)) {} + Exception(Type base, Meta m = Meta()) : TypeBase({std::move(base)}, std::move(m)) {} + Exception(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + std::optional baseType() const { + auto t = childs()[0].tryAs(); + if ( t ) + return type::effectiveType(*t); + + return {}; + } + + bool operator==(const Exception& other) const { return baseType() == other.baseType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/function.h b/hilti/include/ast/types/function.h new file mode 100644 index 000000000..080741fe9 --- /dev/null +++ b/hilti/include/ast/types/function.h @@ -0,0 +1,122 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace type { + +namespace function { + +/** + * A function's flavor diffrentiates between a set of "function-like" + * language element. + */ +enum class Flavor { + Hook, /**< a hook */ + Method, /**< a struct method */ + Standard /**< a normal function */ +}; + +namespace detail { +constexpr util::enum_::Value flavors[] = { + {Flavor::Hook, "hook"}, + {Flavor::Method, "method"}, + {Flavor::Standard, "standard"}, +}; +} // namespace detail + +constexpr auto to_string(Flavor f) { return util::enum_::to_string(f, detail::flavors); } + +namespace flavor { +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, detail::flavors); } +} // namespace flavor + +/** AST node for a result type. */ +class Result : public NodeBase { +public: + Result(Type type, Meta m = Meta()) : NodeBase(nodes(std::move(type)), std::move(m)) {} + + Result() : NodeBase(nodes(node::none), Meta()) {} + + auto type() const { return type::effectiveType(child(0)); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Result& other) const { return type() == other.type(); } +}; + +using Parameter = declaration::Parameter; + +namespace parameter { +using Kind = declaration::parameter::Kind; +} // namespace parameter + +} // namespace function + +class Function : public TypeBase, trait::isParameterized { +public: + Function(Wildcard /*unused*/, Meta m = Meta()) + : TypeBase({function::Result(type::Error(m))}, std::move(m)), _wildcard(true) {} + Function(function::Result result, const std::vector& params, + function::Flavor flavor = function::Flavor::Standard, Meta m = Meta()) + : TypeBase(nodes(std::move(result), util::transform(params, [](auto p) { return Declaration(p); })), + std::move(m)), + _flavor(flavor) {} + + auto result() const { return child(0); } + auto parameters() const { return childs(1, -1); } + auto flavor() const { return _flavor; } + + auto operands() const { + // Would be good to precompute this but type resolution wouldn't + // carry through unfortunately. + return type::OperandList::fromParameters(parameters()); + } + + bool operator==(const Function& other) const { + return result() == other.result() && parameters() == other.parameters(); + } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"flavor", to_string(_flavor)}}; } + +private: + bool _wildcard = false; + function::Flavor _flavor = function::Flavor::Standard; +}; + +/** + * Returns true if two function types are equivalent, even if not + * identical. This for example allows parameter ID to be different. + */ +inline bool areEquivalent(const Function& f1, const Function& f2) { + if ( ! (f1.result() == f2.result()) ) + return false; + + auto p1 = f1.parameters(); + auto p2 = f2.parameters(); + return std::equal(begin(p1), end(p1), begin(p2), end(p2), + [](const auto& p1, const auto& p2) { return areEquivalent(p1, p2); }); +} + +} // namespace type + +inline Node to_node(type::function::Result t) { return Node(std::move(t)); } + +} // namespace hilti diff --git a/hilti/include/ast/types/id.h b/hilti/include/ast/types/id.h new file mode 100644 index 000000000..4ae21091c --- /dev/null +++ b/hilti/include/ast/types/id.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace hilti { +namespace type { + +/** AST node for a resolved type ID. */ +class ResolvedID : public TypeBase, trait::hasDynamicType { +public: + ResolvedID(::hilti::ID id, NodeRef r, Meta m = Meta()) + : TypeBase({std::move(id)}, std::move(m)), _node(std::move(std::move(r))) { + assert(_node && _node->isA()); + } + + auto id() const { return child<::hilti::ID>(0); } + auto declaration() const { + assert(_node); + return _node->as(); + } + auto type() const { + assert(_node); + return _node->as().type(); + } + bool isValid() const { return static_cast(_node); } + const NodeRef& ref() const { return _node; } + + bool operator==(const ResolvedID& other) const { return type() == other.type(); } + + /** Implements the `Type` interface. */ + bool isEqual(const Type& other) const { return type() == type::effectiveType(other); } + /** Implements the `Type` interface. */ + Type effectiveType() const { return type(); } + + /** Implements the `Node` interface. */ + auto properties() const { + return _node ? node::Properties{{"resolved", _node.renderedRid()}} : node::Properties{{}}; + } + +private: + NodeRef _node; +}; + +/** AST node for an unresolved type ID. */ +class UnresolvedID : public TypeBase { +public: + UnresolvedID(::hilti::ID id, Meta m = Meta()) : TypeBase({std::move(id)}, std::move(m)) {} + + auto id() const { return child<::hilti::ID>(0); } + + bool operator==(const UnresolvedID& other) const { return id() == other.id(); } + + // Type interface. + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/integer.h b/hilti/include/ast/types/integer.h new file mode 100644 index 000000000..e7eaca40b --- /dev/null +++ b/hilti/include/ast/types/integer.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +namespace detail { + +// CHECK: IntegerBase = TypeBase +/** Base class for an AST node representing an integer type. */ +class IntegerBase : public TypeBase, trait::isAllocable, trait::isParameterized { +public: + IntegerBase(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} + IntegerBase(int width, Meta m = Meta()) : TypeBase(std::move(m)), _width(width) {} + IntegerBase(Meta m = Meta()) : TypeBase(std::move(m)) {} + + auto width() const { return _width; } + + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"width", _width}}; } + +private: + bool _wildcard = false; + int _width = 0; +}; + +} // namespace detail + +/** AST node for a signed integer type. */ +class SignedInteger : public detail::IntegerBase { +public: + using detail::IntegerBase::IntegerBase; + + bool operator==(const SignedInteger& other) const { return width() == other.width(); } + + /** Implements the `Type` interface. */ + std::vector typeParameters() const; + + /** Implements the `Node` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } +}; + +/** AST node for an unsigned integer type. */ +class UnsignedInteger : public detail::IntegerBase { +public: + using detail::IntegerBase::IntegerBase; + + bool operator==(const UnsignedInteger& other) const { return width() == other.width(); } + + /** Implements the `Type` interface. */ + std::vector typeParameters() const; + + /** Implements the `Node` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/interval.h b/hilti/include/ast/types/interval.h new file mode 100644 index 000000000..86edb1f5f --- /dev/null +++ b/hilti/include/ast/types/interval.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for an interval type. */ +class Interval : public TypeBase, trait::isAllocable { +public: + Interval(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Interval& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/library.h b/hilti/include/ast/types/library.h new file mode 100644 index 000000000..f884b3710 --- /dev/null +++ b/hilti/include/ast/types/library.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace hilti { +namespace type { + +/** + * AST node for a generic type defined just by the runtime library. A library + * type remains mostly opaque to the HILTI language and can't be access + * directly from a HILTI program. Usally, there'll be HILTI-side typedef + * making it accessibile in the `hilti::*` namespace. HILTI assumes the + * library type to be mutable. + */ +class Library : public TypeBase, trait::isAllocable, trait::isMutable { +public: + Library(std::string cxx_name, Meta m = Meta()) : TypeBase(std::move(m)), _cxx_name(std::move(cxx_name)) {} + + const std::string& cxxName() const { return _cxx_name; } + bool operator==(const Library& other) const { return _cxx_name == other._cxx_name; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { + if ( other.cxxID() == _cxx_name ) + return true; + + return node::isEqual(this, other); + } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"cxx_name", _cxx_name}}; } + +private: + std::string _cxx_name; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/list.h b/hilti/include/ast/types/list.h new file mode 100644 index 000000000..1d3f20cc0 --- /dev/null +++ b/hilti/include/ast/types/list.h @@ -0,0 +1,80 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +namespace list { + +/** AST node for a list iterator type. */ +class Iterator : public TypeBase, + trait::isIterator, + trait::isDereferencable, + trait::isAllocable, + trait::isMutable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Iterator(Type ctype, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)) {} + Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Returns the type of the container the iterator is working on. */ + Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type dereferencedType() const { + return (_wildcard || containerType().isWildcard()) ? type::unknown : containerType().elementType(); + } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Iterator& other) const { return dereferencedType() == other.dereferencedType(); } + +private: + bool _wildcard = false; +}; + +} // namespace list + +/** AST node for a list type. */ +class List : public TypeBase, + trait::isAllocable, + trait::isMutable, + trait::isIterable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + List(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} + List(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + /** Implements the `Type` interface. */ + Type iteratorType(bool /* const_ */) const { return list::Iterator(*this, meta()); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const List& other) const { return elementType() == other.elementType(); } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/map.h b/hilti/include/ast/types/map.h new file mode 100644 index 000000000..fcb1bf372 --- /dev/null +++ b/hilti/include/ast/types/map.h @@ -0,0 +1,96 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace type { + +namespace map { + +/** AST node for a map iterator type. */ +class Iterator : public TypeBase, + trait::isIterator, + trait::isDereferencable, + trait::isAllocable, + trait::isMutable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Iterator(Type ctype, bool const_, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)), _const(const_) {} + Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Returns the type of the container the iterator is working on. */ + Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + + /** Returns true if the container elements aren't modifiable. */ + bool isConstant() const { return _const; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type dereferencedType() const; + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"const", _const}}; } + + bool operator==(const Iterator& other) const { return dereferencedType() == other.dereferencedType(); } + +private: + bool _wildcard = false; + bool _const = false; +}; + +} // namespace map + +/** AST node for a map type. */ +class Map : public TypeBase, + trait::isAllocable, + trait::isMutable, + trait::isIterable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Map(Type key, Type value, Meta m = Meta()) : TypeBase({std::move(key), std::move(value)}, std::move(m)) {} + Map(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + Type keyType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(1)); } + /** Implements the `Type` interface. */ + Type iteratorType(bool const_) const { return map::Iterator(*this, const_, meta()); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Map& other) const { + return keyType() == other.keyType() && elementType() == other.elementType(); + } + +private: + bool _wildcard = false; +}; + +namespace map { +inline Type Iterator::dereferencedType() const { + if ( _wildcard || containerType().isWildcard() ) + return type::unknown; + + return type::Tuple({containerType().as().keyType(), containerType().elementType()}); +} +} // namespace map + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/member.h b/hilti/include/ast/types/member.h new file mode 100644 index 000000000..3f59a3ac2 --- /dev/null +++ b/hilti/include/ast/types/member.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +namespace hilti { +namespace type { + +/** AST node for a type representing a member of another type. */ +class Member : public TypeBase, trait::isParameterized { +public: + Member(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({ID("")}, std::move(m)), _wildcard(true) {} + Member(::hilti::ID id, Meta m = Meta()) : TypeBase({std::move(id)}, std::move(m)) {} + + auto id() const { return child<::hilti::ID>(0); } + + bool operator==(const Member& other) const { return id() == other.id(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return std::vector{id()}; } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/network.h b/hilti/include/ast/types/network.h new file mode 100644 index 000000000..d0d88e95f --- /dev/null +++ b/hilti/include/ast/types/network.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a network type. */ +class Network : public TypeBase, trait::isAllocable { +public: + Network(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Network& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/null.h b/hilti/include/ast/types/null.h new file mode 100644 index 000000000..f9375a8ad --- /dev/null +++ b/hilti/include/ast/types/null.h @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a null type. */ +class Null : public TypeBase { +public: + Null(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Null& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/operand-list.h b/hilti/include/ast/types/operand-list.h new file mode 100644 index 000000000..4a6a6b89f --- /dev/null +++ b/hilti/include/ast/types/operand-list.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti { +namespace type { + +/** + * AST node for a type representing a list of function/method operands. This + * is an internal type used for overload resolution, it's nothing actually + * instantiated by a HILTI program. That's also why we don't use any child + * nodes, but store the operands directly. + */ +class OperandList : public TypeBase { +public: + OperandList(std::vector operands) : _operands(std::move(operands)) {} + + const auto& operands() const { return _operands; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const OperandList& other) const { return operands() == other.operands(); } + + static OperandList fromParameters(const std::vector& params) { + std::vector ops; + + for ( const auto& p : params ) { + operator_::Operand op = {.id = p.id(), + .type = type::setConstant(p.type(), p.isConstant()), + .optional = p.default_().has_value(), + .default_ = p.default_()}; + + ops.push_back(std::move(op)); + } + + return type::OperandList(std::move(ops)); + } + +private: + std::vector _operands; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/optional.h b/hilti/include/ast/types/optional.h new file mode 100644 index 000000000..bb47259e0 --- /dev/null +++ b/hilti/include/ast/types/optional.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for an "optional" type. */ +class Optional : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isDereferencable { +public: + Optional(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Optional(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} + + Type dereferencedType() const { + if ( auto t = childs()[0].tryAs() ) + return *t; + + return type::unknown; + } + + bool operator==(const Optional& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/port.h b/hilti/include/ast/types/port.h new file mode 100644 index 000000000..7ffce0a3c --- /dev/null +++ b/hilti/include/ast/types/port.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a port type. */ +class Port : public TypeBase, trait::isAllocable { +public: + Port(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Port& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/real.h b/hilti/include/ast/types/real.h new file mode 100644 index 000000000..0a190e07f --- /dev/null +++ b/hilti/include/ast/types/real.h @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a floating point type. */ +class Real : public TypeBase, trait::isAllocable { +public: + Real(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Real& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/reference.h b/hilti/include/ast/types/reference.h new file mode 100644 index 000000000..9abe3dda4 --- /dev/null +++ b/hilti/include/ast/types/reference.h @@ -0,0 +1,117 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +/* + * AST node for a `strong_ref` type. + */ +class StrongReference : public TypeBase, + trait::isAllocable, + trait::isParameterized, + trait::isDereferencable, + trait::isReferenceType { +public: + StrongReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + StrongReference(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} + StrongReference(Type ct, bool treat_as_non_constant, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) { + if ( treat_as_non_constant ) + _state().flags -= type::Flag::Constant; + } + + Type dereferencedType() const { + if ( auto t = childs()[0].tryAs() ) + return type::effectiveType(*t); + + return type::unknown; + } + + bool operator==(const StrongReference& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +/** AST node for a `weak_ref` type. */ +class WeakReference : public TypeBase, + trait::isAllocable, + trait::isParameterized, + trait::isDereferencable, + trait::isReferenceType { +public: + WeakReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + WeakReference(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} + + Type dereferencedType() const { + if ( auto t = childs()[0].tryAs() ) + return type::effectiveType(*t); + + return type::unknown; + } + + bool operator==(const WeakReference& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +/** AST node for a `val_ref` type. */ +class ValueReference : public TypeBase, + trait::isAllocable, + trait::isParameterized, + trait::isDereferencable, + trait::isReferenceType { +public: + ValueReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + ValueReference(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} + + Type dereferencedType() const { + if ( auto t = childs()[0].tryAs() ) + return type::effectiveType(*t); + + return type::unknown; + } + + bool operator==(const ValueReference& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/regexp.h b/hilti/include/ast/types/regexp.h new file mode 100644 index 000000000..e350e20b8 --- /dev/null +++ b/hilti/include/ast/types/regexp.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a regexp type. */ +class RegExp : public TypeBase, trait::isAllocable, trait::isRuntimeNonTrivial { +public: + RegExp(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const RegExp& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/result.h b/hilti/include/ast/types/result.h new file mode 100644 index 000000000..8544170f6 --- /dev/null +++ b/hilti/include/ast/types/result.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a "result" type. */ +class Result : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isDereferencable { +public: + Result(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Result(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} + + Type dereferencedType() const { + if ( auto t = childs()[0].tryAs() ) + return *t; + + return type::unknown; + } + + bool operator==(const Result& other) const { return dereferencedType() == other.dereferencedType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/set.h b/hilti/include/ast/types/set.h new file mode 100644 index 000000000..718570b1f --- /dev/null +++ b/hilti/include/ast/types/set.h @@ -0,0 +1,84 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +namespace set { + +/** AST node for a set iterator type. */ +class Iterator : public TypeBase, + trait::isIterator, + trait::isDereferencable, + trait::isAllocable, + trait::isMutable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Iterator(Type ctype, bool const_, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)), _const(const_) {} + Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Returns the type of the container the iterator is working on. */ + Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + + /** Returns true if the container elements aren't modifiable. */ + bool isConstant() const { return _const; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type dereferencedType() const { + return (_wildcard || containerType().isWildcard()) ? type::unknown : containerType().elementType(); + } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"const", _const}}; } + + bool operator==(const Iterator& other) const { return dereferencedType() == other.dereferencedType(); } + +private: + bool _wildcard = false; + bool _const = false; +}; + +} // namespace set + +/** AST node for a set type. */ +class Set : public TypeBase, + trait::isAllocable, + trait::isMutable, + trait::isIterable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Set(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} + Set(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + /** Implements the `Type` interface. */ + Type iteratorType(bool const_) const { return set::Iterator(*this, const_, meta()); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Set& other) const { return elementType() == other.elementType(); } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/stream.h b/hilti/include/ast/types/stream.h new file mode 100644 index 000000000..de80df933 --- /dev/null +++ b/hilti/include/ast/types/stream.h @@ -0,0 +1,89 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +namespace stream { + +/** AST node for a stream iterator type. */ +class Iterator : public TypeBase, + trait::isIterator, + trait::isDereferencable, + trait::isAllocable, + trait::isMutable, + trait::isRuntimeNonTrivial { +public: + Iterator(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Iterator& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type dereferencedType() const; + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +/** AST node for a stream view type. */ +class View : public TypeBase, trait::isView, trait::isIterable, trait::isAllocable, trait::isRuntimeNonTrivial { +public: + View(Meta m = Meta()); + + bool operator==(const View& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return type::UnsignedInteger(8); } + /** Implements the `Type` interface. */ + Type iteratorType(bool /* const_ */) const { return stream::Iterator(meta()); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace stream + +/** AST node for a stream type. */ +class Stream : public TypeBase, + trait::isAllocable, + trait::isMutable, + trait::isIterable, + trait::isViewable, + trait::isRuntimeNonTrivial { +public: + Stream(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Stream& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return type::UnsignedInteger(8); } + + /** Implements the `Type` interface. */ + Type iteratorType(bool /* const_ */) const { return stream::Iterator(meta()); } + /** Implements the `Type` interface. */ + Type viewType() const { return stream::View(meta()); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + std::optional _etype; +}; + +namespace detail::stream { +inline Node element_type = Node(type::UnsignedInteger(8, Location())); +} // namespace detail::stream + +inline Type stream::Iterator::dereferencedType() const { return type::UnsignedInteger(8); } +inline stream::View::View(Meta m) : TypeBase({type::Stream()}, std::move(m)) {} + +} // namespace type + +} // namespace hilti diff --git a/hilti/include/ast/types/string.h b/hilti/include/ast/types/string.h new file mode 100644 index 000000000..01ec9c793 --- /dev/null +++ b/hilti/include/ast/types/string.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a string type. */ +class String : public TypeBase, trait::isAllocable { +public: + String(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const String& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/struct.h b/hilti/include/ast/types/struct.h new file mode 100644 index 000000000..de0009beb --- /dev/null +++ b/hilti/include/ast/types/struct.h @@ -0,0 +1,196 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace type { + +namespace struct_ { +/** AST node for a struct field. */ +class Field : public NodeBase { +public: + Field() : NodeBase({ID(""), type::unknown, node::none}, Meta()) {} + Field(ID id, Type t, std::optional attrs = {}, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(t), std::move(attrs)), std::move(m)) {} + Field(ID id, ::hilti::function::CallingConvention cc, type::Function ft, std::optional attrs = {}, + Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(ft), std::move(attrs)), std::move(m)), _cc(cc) {} + + auto id() const { return child(0); } + auto type() const { return type::effectiveType(child(1)); } + auto callingConvention() const { return _cc; } + auto attributes() const { return childs()[2].tryAs(); } + + std::optional default_() const { + if ( auto a = AttributeSet::find(attributes(), "&default") ) + return *a->valueAs(); + + return {}; + } + + auto isInternal() const { + return AttributeSet::find(attributes(), "&internal"); + ; + } + auto isOptional() const { return AttributeSet::find(attributes(), "&optional"); } + auto isStatic() const { return AttributeSet::find(attributes(), "&static"); } + auto isNoEmit() const { return AttributeSet::find(attributes(), "&no-emit"); } + + /** Internal method for use by builder API only. */ + auto& _typeNode() { return childs()[1]; } + + /** Implements the `Node` interface. */ + auto properties() const { + return node::Properties{ + {"cc", to_string(_cc)}, + }; + } + + bool operator==(const Field& other) const { + return id() == other.id() && type() == other.type() && attributes() == other.attributes() && _cc == other._cc; + } + + /** + * Copies an existing field but replaces its attributes. + * + * @param f original field + * @param attrs new attributes + * @return new field with attributes replaced + */ + static Field setAttributes(const Field& f, const AttributeSet& attrs) { + auto x = Field(f); + x.childs()[2] = attrs; + return x; + } + +private: + ::hilti::function::CallingConvention _cc = ::hilti::function::CallingConvention::Standard; +}; + +inline Node to_node(Field f) { return Node(std::move(f)); } + +} // namespace struct_ + +/** AST node for a struct type. */ +class Struct : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isMutable { +public: + Struct(std::vector fields, Meta m = Meta()) + : TypeBase(nodes(node::none, std::move(fields)), std::move(m)) { + _state().flags += type::Flag::NoInheritScope; + } + + Struct(std::vector params, std::vector fields, Meta m = Meta()) + : TypeBase(nodes(node::none, std::move(fields), + util::transform(params, + [](auto p) { return type::function::Parameter::setIsStructParameter(p); })), + std::move(m)) { + _state().flags += type::Flag::NoInheritScope; + } + + Struct(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _wildcard(true) { + _state().flags += type::Flag::NoInheritScope; + } + + auto parameters() const { return childsOfType(); } + + std::vector parameterNodes() { + std::vector params; + for ( auto& c : childs() ) { + if ( c.isA() ) + params.emplace_back(NodeRef(c)); + } + return params; + } + + auto fields() const { return childsOfType(); } + + auto types() const { + std::vector types; + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) + types.push_back(c->as().type()); + + return types; + } + + auto ids() const { + std::vector ids; + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) + ids.push_back(c->as().id()); + + return ids; + } + + std::optional field(const ID& id) const { + for ( auto f : fields() ) { + if ( f.id() == id ) + return f; + } + + return {}; + } + + auto fields(const ID& id) const { + std::vector x; + + for ( const auto& f : fields() ) { + if ( f.id() == id ) + x.push_back(f); + } + + return x; + } + + bool operator==(const Struct& other) const { + if ( typeID() && other.typeID() ) + return *typeID() == *other.typeID(); + + return fields() == other.fields(); + } + + /** For internal use by the builder API only. */ + auto _fieldNodes() { return nodesOfType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { + std::vector params; + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) + params.emplace_back(c->as().type()); + return params; + } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Copies an existing type and adds a new field to the copy. + * + * @param s original type + * @param f field to add + * @return new typed with field added + */ + static Struct addField(const Struct& s, struct_::Field f) { + auto x = Type(s)._clone().as(); + x.addChild(std::move(f)); + return x; + } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/time.h b/hilti/include/ast/types/time.h new file mode 100644 index 000000000..962f97ce6 --- /dev/null +++ b/hilti/include/ast/types/time.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a time type. */ +class Time : public TypeBase, trait::isAllocable { +public: + Time(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Time& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/tuple.h b/hilti/include/ast/types/tuple.h new file mode 100644 index 000000000..128ff8c36 --- /dev/null +++ b/hilti/include/ast/types/tuple.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +/** AST node for a tuple type. */ +class Tuple : public TypeBase, trait::isAllocable, trait::isParameterized { +public: + Tuple(std::vector t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} + Tuple(std::vector> t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} + Tuple(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} + + auto types() const { return childsOfType(); } + std::vector ids() const; + auto elements() const { return util::zip2(ids(), types()); } + std::optional> elementByID(const ID& id); + + bool operator==(const Tuple& other) const { + if ( _wildcard || other._wildcard ) + return _wildcard && other._wildcard; + + return types() == other.types() && ids() == other.ids(); + } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"wildcard", _wildcard}}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/type.h b/hilti/include/ast/types/type.h new file mode 100644 index 000000000..a97db8c11 --- /dev/null +++ b/hilti/include/ast/types/type.h @@ -0,0 +1,35 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +/** AST node for a type representing a type value. */ +class Type_ : public TypeBase, trait::isParameterized { +public: + Type_(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} + Type_(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} + + auto typeValue() const { return _wildcard ? type::Any() : type::effectiveType(child(0)); } + + bool operator==(const Type_& other) const { return typeValue() == other.typeValue(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/union.h b/hilti/include/ast/types/union.h new file mode 100644 index 000000000..aa9a9e96e --- /dev/null +++ b/hilti/include/ast/types/union.h @@ -0,0 +1,149 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace type { + +namespace union_ { +/** AST node for a struct field. */ +class Field : public NodeBase { +public: + Field() : NodeBase({ID(""), type::unknown, node::none}, Meta()) {} + Field(ID id, Type t, std::optional attrs = {}, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(t), std::move(attrs)), std::move(m)) {} + + auto id() const { return child(0); } + auto type() const { return type::effectiveType(child(1)); } + auto attributes() const { return childs()[2].tryAs(); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Field& other) const { + return id() == other.id() && type() == other.type() && attributes() == other.attributes(); + } + + /** + * Copies an existing field but replaces its attributes. + * + * @param f original field + * @param attrs new attributes + * @return new field with attributes replaced + */ + static Field setAttributes(const Field& f, const AttributeSet& attrs) { + auto x = Field(f); + x.childs()[2] = attrs; + return x; + } +}; + +inline Node to_node(Field f) { return Node(std::move(f)); } + +} // namespace union_ + +/** AST node for a struct type. */ +class Union : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isMutable { +public: + Union(std::vector fields, Meta m = Meta()) + : TypeBase(nodes(node::none, std::move(fields)), std::move(m)) {} + Union(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _wildcard(true) {} + + auto fields() const { return childsOfType(); } + + auto types() const { + std::vector types; + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) + types.push_back(c->as().type()); + + return types; + } + + auto ids() const { + std::vector ids; + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) + ids.push_back(c->as().id()); + + return ids; + } + + std::optional field(const ID& id) const { + for ( auto f : fields() ) { + if ( f.id() == id ) + return f; + } + + return {}; + } + + unsigned int index(const ID& id) const { + for ( auto [i, f] : util::enumerate(fields()) ) { + if ( f.id() == id ) + return i + 1; + } + + return 0; + } + + auto fields(const ID& id) const { + std::vector x; + + for ( const auto& f : fields() ) { + if ( f.id() == id ) + x.push_back(f); + } + + return x; + } + + bool operator==(const Union& other) const { + if ( typeID() && other.typeID() ) + return *typeID() == *other.typeID(); + + return fields() == other.fields(); + } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { + std::vector params; + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) + params.emplace_back(c->as().type()); + return params; + } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Copies an existing type and adds a new field to the copy. + * + * @param s original type + * @param f field to add + * @return new typed with field added + */ + static Union addField(const Union& s, union_::Field f) { + auto x = Type(s)._clone().as(); + x.addChild(std::move(f)); + return x; + } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/unknown.h b/hilti/include/ast/types/unknown.h new file mode 100644 index 000000000..bb2d90e45 --- /dev/null +++ b/hilti/include/ast/types/unknown.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for an unknown place-holder type. */ +class Unknown : public TypeBase, public util::type_erasure::trait::Singleton { +public: + bool operator==(const Unknown& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Wrapper around constructor so that we can make it private. Don't use + * this, use the singleton `type::unknown` instead. + */ + static Unknown create(Meta m = Meta()) { return Unknown(std::move(m)); } + +private: + Unknown(Meta m = Meta()) : TypeBase(std::move(m)) {} +}; + +/** Singleton. */ +static const Type unknown = Unknown::create(Location("")); + +} // namespace type + +inline const Node& to_node(const type::Unknown& /* t */) { + static Node unknown = Type(type::unknown); + return unknown; +} + +} // namespace hilti diff --git a/hilti/include/ast/types/vector.h b/hilti/include/ast/types/vector.h new file mode 100644 index 000000000..6e9878876 --- /dev/null +++ b/hilti/include/ast/types/vector.h @@ -0,0 +1,84 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti { +namespace type { + +namespace vector { + +/** AST node for a vector iterator type. */ +class Iterator : public TypeBase, + trait::isIterator, + trait::isDereferencable, + trait::isAllocable, + trait::isMutable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Iterator(Type ctype, bool const_, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)), _const(const_) {} + Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Returns the type of the container the iterator is working on. */ + Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + + /** Returns true if the container elements aren't modifiable. */ + bool isConstant() const { return _const; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type dereferencedType() const { + return (_wildcard || containerType().isWildcard()) ? type::unknown : containerType().elementType(); + } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"const", _const}}; } + + bool operator==(const Iterator& other) const { return dereferencedType() == other.dereferencedType(); } + +private: + bool _wildcard = false; + bool _const = false; +}; + +} // namespace vector + +/** AST node for a vector type. */ +class Vector : public TypeBase, + trait::isAllocable, + trait::isMutable, + trait::isIterable, + trait::isRuntimeNonTrivial, + trait::isParameterized { +public: + Vector(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} + Vector(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + /** Implements the `Type` interface. */ + Type iteratorType(bool const_) const { return vector::Iterator(*this, const_, meta()); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Type` interface. */ + auto typeParameters() const { return childs(); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Vector& other) const { return elementType() == other.elementType(); } + +private: + bool _wildcard = false; +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/ast/types/void.h b/hilti/include/ast/types/void.h new file mode 100644 index 000000000..8bc9bf4ef --- /dev/null +++ b/hilti/include/ast/types/void.h @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace type { + +/** AST node for a void type. */ +class Void : public TypeBase { +public: + Void(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Void& /* other */) const { return true; } + + // Type interface. + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/include/base/cache.h b/hilti/include/base/cache.h new file mode 100644 index 000000000..e71ff361a --- /dev/null +++ b/hilti/include/base/cache.h @@ -0,0 +1,77 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace util { + +/** Simple cache to remember a computed value for a given key. */ +template +class Cache { +public: + using Callback1 = std::function; + using Callback2 = std::function; + + Cache() = default; + + /** Returns true if the cache has an entry for a given key. */ + bool has(const Key& key) const { return _cache.find(key) != _cache.end(); } + + /** + * Returns the value for a given key, or optionally a default if not + * found. Returning the default won't modify the cache. + */ + std::optional get(const Key& key, std::optional default_ = {}) const { + if ( auto i = _cache.find(key); i != _cache.end() ) + return i->second; + + return std::move(default_); + } + + /** + * Returns the value for a given key if it exists; or, if not, executes a + * callback to compute a value. In the latter case the computed value + * will be inserted into the cache before it's returned. + */ + const Value& getOrCreate(const Key& key, const Callback1& cb) { + if ( auto i = _cache.find(key); i != _cache.end() ) + return i->second; + + return put(key, cb()); + } + + /** + * Returns the value for a given key if it exists; or, if not, executes a + * a couple callbacks to compute a value. This splits the computation + * into two parts to handle cases where it may recurse: the first + * callback computes a prelimary value *v* that will be inserted into the + * cache immediately. It will then be passed to the second callback to + * compute the final value. If that second callback accesses the cache + * with the same key during its operation, it will find *v*. The 2nd + * callbacks result will update the cache on completion, although usually + * it will probably just return *v* again to stay consistent. + * + */ + const Value& getOrCreate(const Key& key, const Callback1& cb1, const Callback2& cb2) { + if ( auto i = _cache.find(key); i != _cache.end() ) + return i->second; + + _cache[key] = cb1(); + return _cache[key] = cb2(_cache[key]); + } + + /** Stores a value for a key in the cache. */ + const Value& put(const Key& key, Value value) { return _cache[key] = std::move(value); } + + /** Removes an item from the cache. */ + void remove(const Key& key) { _cache.erase(key); } + +private: + std::map _cache; +}; + +} // namespace util diff --git a/hilti/include/base/code-formatter.h b/hilti/include/base/code-formatter.h new file mode 100644 index 000000000..3b55335bf --- /dev/null +++ b/hilti/include/base/code-formatter.h @@ -0,0 +1,116 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +#include + +namespace hilti { + +/** + * Base class providing support for generating "C-style" code. + * + * The class handles basic formatting, such as code indentation and white + * space insertion. The main output method for user code is `printString()`. + * For most of the formatting methods, there are corresponding i/o stream + * manipulators so that one can writem, e.g., `my_formatter << eol();`. + */ +class CodeFormatter { +public: + /** @param comment string beginning a comment line in the target language */ + explicit CodeFormatter(std::string comment = "//") : _comment(std::move(comment)) {} + ~CodeFormatter() = default; + + /** Writes all output generated so far to an external stream. */ + bool output(std::ostream& out) { return util::copyStream(_out, out); } + + /** Returns a string representation of all output generated so far. */ + auto str() const { return _out.str(); } + + /** Signals the beginning of a new line. */ + void next(); + + /** Inserts an empty line as a separator. */ + void separator(); + + /**< Signals the end of a line. This will insert a newline. */ + void eol(); + + /**< Signals the end of a statement. This will insert both a semicolon and a newline. */ + void eos(); + + /** Surrounds a string with quotation mark and escapes it appropiately. */ + void quoted(const std::string& s); + + /** Inserts a comment line, prefixing it with the comment prefix. */ + void comment(const std::string& s); + + /** Increates the indentation by one level. */ + void indent() { _indent += 1; } + + /** Decreates the indentation by one level. */ + void dedent() { _indent -= 1; } + + /** Returns an stream with the output so far. */ + auto& stream() { return _out; } + + /** Adds a string to the output. */ + CodeFormatter& printString(const std::string& s); + + CodeFormatter(const CodeFormatter&) = delete; + CodeFormatter(CodeFormatter&&) = delete; + CodeFormatter& operator=(const CodeFormatter& f) = delete; + CodeFormatter& operator=(CodeFormatter&& f) = delete; + +private: + std::stringstream _out; + std::string _comment; + + int _indent = 0; + bool _did_sep = true; + bool _at_bol = true; + bool _in_comment = false; +}; + +namespace code_formatter { +class isManipulator {}; +} // namespace code_formatter + +#define __DEFINE_MANIPULATOR0(x) \ + template \ + class x : isManipulator { \ + public: \ + Formatter& operator()(Formatter& f) const { \ + f.x(); \ + return f; \ + } \ + }; + +#define __DEFINE_MANIPULATOR1(x, t) \ + template \ + class x : isManipulator { \ + t _t; \ + \ + public: \ + x(t _t) : _t(std::move(_t)) {} \ + Formatter& operator()(Formatter& f) const { \ + f.x(_t); \ + return f; \ + } \ + }; + +namespace code_formatter { +__DEFINE_MANIPULATOR0(dedent) +__DEFINE_MANIPULATOR0(eol) +__DEFINE_MANIPULATOR0(eos) +__DEFINE_MANIPULATOR0(indent) +__DEFINE_MANIPULATOR0(separator) +__DEFINE_MANIPULATOR1(quoted, std::string) +__DEFINE_MANIPULATOR1(comment, std::string) +} // namespace code_formatter + +} // namespace hilti diff --git a/hilti/include/base/id-base.h b/hilti/include/base/id-base.h new file mode 100644 index 000000000..b1c7dc6fb --- /dev/null +++ b/hilti/include/base/id-base.h @@ -0,0 +1,166 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti::detail { + +using normalizer_func = std::string (*)(std::string); +inline std::string identity_normalizer(std::string s) { return s; } + +/** + * Base class for representing scoped language IDs. It provides a number of + * standard accesasorsd and manipulators to support operations on/with + * namespaces. This class assumes that namespaces are seperated with `::`. + * + * @tparam Derived name of the class deriving from this one (CRTP). + * @tparam N a function that may preprocess/normalize all ID components before storing them + * + */ +template +class IDBase { +public: + IDBase() = default; + IDBase(const char* s) : _id(N(s)) {} + explicit IDBase(std::string s) : _id(N(std::move(s))) {} + + /** Concatenates multiple strings into a single ID, separating them with `::`. */ + template)>> + explicit IDBase(const T&... s) : _id(N(util::join({s...}, "::"))) {} + + /** Concatenates multiple strings into a single ID, separating them with `::`. */ + IDBase(const std::initializer_list& x) : _id(util::join(x, "::")) {} + + /** Returns the ID's full name as a string. */ + auto str() const { return _id; } + + /** Returns the ID's namespace. That's everything except the local part. */ + Derived namespace_() const { return Derived(util::rsplit1(_id, "::").first); } + + /** Returns the ID local part. */ + Derived local() const { return Derived(util::rsplit1(_id, "::").second); } + + /** Returns true if the ID's value has length zero. */ + bool empty() const { return _id.empty(); } + + /** + * Returns a new ID containing just single component of the path;s of the + * ID. Indices are zero-based and, if negative, counted from the end + * Python-style. + * + * @param i index of path component to return + */ + Derived sub(int i) const { + auto x = util::split(_id, "::"); + + if ( i < 0 ) + i = x.size() + i; + + return Derived(i >= 0 && static_cast(i) < x.size() ? x[i] : ""); + } + + /** + * Returns a new ID containing a subpath of the ID. Indices are + * zero-based and, if negative, counted from the end Python-style. + * + * @param from 1st index to include + * @param to one beyond last index to include + */ + Derived sub(int from, int to) const { + return Derived(util::join(util::slice(util::split(_id, "::"), from, to), "::")); + } + + /** + * Returns a new ID containing the a subpath of the ID, starting at the + * beginning. + * + * @param n number of path components to include + */ + Derived firstN(int n) const { return Derived(sub(0, -1 - n)); } + + /** + * Returns a new ID containing the a subpath of the ID, starting at the + * end. + * + * @param n number of path components to include + */ + Derived lastN(int n) const { return Derived(sub(-1 - n, -1)); } + + /** + * "Rebases" the ID relative to another one. + * + * If the ID already starts with `root`, the remaining part is returned. + * If not, the returned value is `root` plus the ID. + */ + Derived relativeTo(const IDBase& root) const { + if ( _id == root._id ) + return Derived(); + + if ( ! util::startsWith(_id, root._id + "::") ) + return Derived(root, _id); + + return Derived(_id.substr(root._id.size() + 2)); + } + + /** Concantenates two IDs, separating them wiht `::`. */ + Derived operator+(const std::string& other) const { + Derived n(_id); + n += N(other); + return n; + } + + /** Concantenates two IDs, separating them wiht `::`. */ + Derived operator+(const IDBase& other) const { + Derived n(_id); + n += other; + return n; + } + + /** Appends an ID, separating it with `::`. */ + Derived& operator+=(std::string other) { + if ( ! other.empty() ) { + if ( _id.empty() ) + _id = N(std::move(other)); + else + _id += "::" + N(std::move(other)); + } + + return static_cast(*this); + } + + /** Appends an ID, separating it with `::`. */ + Derived& operator+=(const IDBase& other) { + if ( ! other._id.empty() ) { + if ( other._id.empty() ) + _id = other._id; + else + _id += "::" + other._id; + } + + return static_cast(*this); + } + + bool operator==(const IDBase& other) const { return _id == other._id; }; + bool operator!=(const IDBase& other) const { return _id != other._id; }; + bool operator==(const std::string& other) const { return _id == N(other); } + bool operator!=(const std::string& other) const { return _id != N(other); } + bool operator<(const IDBase& other) const { return _id < other._id; }; + + explicit operator bool() const { return ! empty(); } + operator std::string() const { return _id; } + +protected: + struct AlreadyNormalized {}; + + /** no normalization */ + IDBase(std::string id, AlreadyNormalized /*unused*/) : _id(std::move(id)) {} + +private: + std::string _id; +}; + +} // namespace hilti::detail diff --git a/hilti/include/base/logger.h b/hilti/include/base/logger.h new file mode 100644 index 000000000..e7a8a636d --- /dev/null +++ b/hilti/include/base/logger.h @@ -0,0 +1,244 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +/** Macro around `Logger::__debug` that avoids evaluation of the arguments if nothing is going to get logged. */ +#define HILTI_DEBUG(dbg, ...) \ + { \ + if ( ::hilti::logger().isEnabled(dbg) ) \ + ::hilti::logger()._debug(dbg, __VA_ARGS__); \ + } + +namespace hilti { +namespace logging { + +/** + * A named debug stream. Debugging output can be send to it and will be + * written out during runtime by the `Logger` if it has enabled the stream. + */ +class DebugStream { +public: + /** + * @param name name of the stream, which must be unique across all stream + */ + explicit DebugStream(const std::string& name); + bool operator<(const DebugStream& other) const { return _id < other._id; } + auto name() const { return _name; } + + /** Returns the names of all available debug streams. */ + static std::vector all(); + + /** Returns the stream for a given name. The stream must exist. */ + static const auto& streamForName(const std::string& s) { return _all.at(s); } + +private: + uint64_t _id; + std::string _name; + static inline std::map _all; +}; + +namespace debug {} // namespace debug + +/** Logging level. */ +enum class Level { Debug, Info, Warning, Error, FatalError, InternalError }; + +namespace detail { +constexpr util::enum_::Value levels[] = { + {Level::Debug, "debug"}, + {Level::Info, "info"}, + {Level::Warning, "warning"}, + {Level::Error, "error"}, + {Level::FatalError, "fatal-error"}, + {Level::InternalError, "internal-error"}, +}; +} // namespace detail + +constexpr auto to_string(Level m) { return util::enum_::to_string(m, detail::levels); } + +namespace level { +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, detail::levels); } +} // namespace level + +/** Ostream-variant that forwards output to the central logger. */ +class Stream : public std::ostream { +private: + class Buffer : public std::stringbuf { + public: + Buffer(logging::Level level); + Buffer(logging::DebugStream dbg); + + private: + int overflow(int ch) final; + int sync() final; + + Level _level; + std::optional _dbg; + std::string _buffer; + }; + +public: + /** Creates a stream that sends output to a given logging level. */ + Stream(logging::Level level) : std::ostream(&_buf), _buf(level) {} + + /** Creates a stream that sends output to a given debug stream. */ + Stream(logging::DebugStream dbg) : std::ostream(&_buf), _buf(std::move(dbg)) {} + +private: + Buffer _buf; +}; + +} // namespace logging + +class Logger; + +/** + * Returns the global logger. A default logger singleton is created at + * startup. A custom logger can be set through `setLogger()`. + */ +inline Logger& logger(); + +/** + * Sets a new logger as the global singleton. Returns the previous one. + */ +extern std::unique_ptr setLogger(std::unique_ptr logger); + +/** Logging system. */ +class Logger { +public: + Logger(std::ostream& output_std = std::cerr, std::ostream& output_debug = std::cerr) + : _output_std(output_std), _output_debug(output_debug) {} + + void log(logging::Level level, const std::string& msg, const Location& l = location::None); + + void info(const std::string& msg, const Location& l = location::None); + void warning(const std::string& msg, const Location& l = location::None); + void error(const std::string& msg, const Location& l = location::None); + void error(const std::string& msg, const std::vector& context, const Location& l = location::None); + void fatalError(const std::string& msg, const Location& l = location::None) __attribute__((noreturn)); + void internalError(const std::string& msg, const Location& l = location::None) __attribute__((noreturn)); + + /** Use HILTI_DEBUG(...) instead. */ + void _debug(const logging::DebugStream& dbg, const std::string& msg, const Location& l = location::None); + + template + void log(std::string msg, const T& n) { + log(msg, to_node(n).location()); + } + + template + void info(std::string msg, const T& n) { + info(msg, to_node(n).location()); + } + + template + void warning(std::string msg, const T& n) { + warning(msg, to_node(n).location()); + } + + template + void error(std::string msg, const T& n) { + error(msg, to_node(n).location()); + } + + template + void error(std::string msg, std::vector context, const T& n) { + error(msg, context, to_node(n).location()); + } + + template + void error(Result r, const T& n) { + error(r.error().description(), to_node(n).location()); + } + + template + __attribute__((noreturn)) void fatalError(std::string msg, const T& n) { + fatalError(msg, to_node(n).location()); + } + + template + __attribute__((noreturn)) void internalError(std::string msg, const T& n) { + internalError(msg, to_node(n).location()); + } + + template + void debug(const logging::DebugStream& dbg, std::string msg, const T& n) { + debug(dbg, msg, to_node(n).location()); + } + + void debugEnable(const logging::DebugStream& dbg); + bool debugEnable(const std::string& dbg); + void debugDisable(const logging::DebugStream& dbg) { _debug_streams.erase(dbg); } + bool debugDisable(const std::string& dbg); + + bool isEnabled(const logging::DebugStream& dbg) { return _debug_streams.find(dbg) != _debug_streams.end(); } + + void debugPushIndent(const logging::DebugStream& dbg) { + if ( isEnabled(dbg) ) + _debug_streams[dbg] += 1; + } + + void debugPopIndent(const logging::DebugStream& dbg) { + if ( isEnabled(dbg) ) + _debug_streams[dbg] -= 1; + } + + int errors() const { return _errors; } + int warnings() const { return _warnings; } + + void reset() { _errors = _warnings = 0; } + +protected: + void report(std::ostream& output, logging::Level level, int indent, const std::string& addl, const std::string& msg, + const Location& l) const; + +private: + friend Logger& logger(); // NOLINT + friend std::unique_ptr setLogger(std::unique_ptr logger); // NOLINT + + std::ostream& _output_std = std::cerr; + std::ostream& _output_debug = std::cerr; + + int _warnings = 0; + int _errors = 0; + + std::map _debug_streams; + + static std::unique_ptr _singleton; +}; + +inline Logger& logger() { + if ( ! Logger::_singleton ) + Logger::_singleton = std::make_unique(); + + return *Logger::_singleton; +} + +namespace logging { + +/** + * Helper class that increases debug indent on construction, and decreases it + * again on destruction. + */ +class DebugPushIndent { +public: + DebugPushIndent(const logging::DebugStream& dbg) : dbg(dbg) { logger().debugPushIndent(dbg); } + ~DebugPushIndent() { logger().debugPopIndent(dbg); } + + DebugPushIndent() = delete; + DebugPushIndent(const DebugPushIndent&) = delete; + DebugPushIndent(DebugPushIndent&&) noexcept = delete; + DebugPushIndent& operator=(const DebugPushIndent&) = delete; + DebugPushIndent& operator=(DebugPushIndent&&) noexcept = delete; + +private: + const logging::DebugStream& dbg; +}; + +} // namespace logging + +} // namespace hilti diff --git a/hilti/include/base/result.h b/hilti/include/base/result.h new file mode 100644 index 000000000..e3d07fdb4 --- /dev/null +++ b/hilti/include/base/result.h @@ -0,0 +1,18 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti { +namespace result { +using Error = hilti::rt::result::Error; +using NoResult = hilti::rt::result::NoResult; +} // namespace result + +template +using Result = hilti::rt::Result; + +struct Nothing {}; + +} // namespace hilti diff --git a/hilti/include/base/timing.h b/hilti/include/base/timing.h new file mode 100644 index 000000000..242177424 --- /dev/null +++ b/hilti/include/base/timing.h @@ -0,0 +1,156 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** API to measure execution times and frequency for code area. */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace util { + +namespace timing { + +using Clock = std::chrono::high_resolution_clock; +using Time = Clock::time_point; +using Duration = Clock::duration; + +class Collector; +class Ledger; + +namespace detail { + +/** Singleton object managing all timer state. */ +class Manager { +public: + /** + * Renders a summary of execution statistics for all currently existing + * `Ledger` objects. + */ + static void summary(std::ostream& out); + + /** + * Returns a pointer to a global singleon manager instance. This returns + * a shared_ptr so that ledgers can store that to ensure the global + * singleton doesn't get destroyed at exit before they go away, too. + */ + static std::shared_ptr singleton(); + +protected: + friend Collector; + friend Ledger; + + Manager() : _created(Clock::now()) {} + + void register_(Ledger* ledger); + void unregister(Ledger* ledger); + Ledger* newLedger(const std::string& name); + +private: + Time _created; + std::unordered_map _all_ledgers; + std::list _our_ledgers; +}; + +} // namespace detail + +inline void summary(std::ostream& out) { detail::Manager::summary(out); } + +/** Maintains measurements of execution time and frequency for one code area. */ +class Ledger { +public: + Ledger(std::string name) : _name(std::move(name)), _manager(detail::Manager::singleton()) { + _manager->register_(this); + } + ~Ledger() { _manager->unregister(this); } + + Ledger() = delete; + Ledger(const Ledger&) = default; + Ledger(Ledger&&) noexcept = default; + Ledger& operator=(const Ledger&) = delete; + Ledger& operator=(Ledger&&) noexcept = delete; + + const std::string& name() const { return _name; } + + void summary(std::ostream& out) const; + +protected: + friend class Collector; + friend class detail::Manager; + + void start() { + if ( _level < 0 ) + return; + + if ( ++_level != 1 ) + return; + + assert(_time_started == Time()); + _time_started = Clock::now(); + } + + void stop() { + if ( _level < 0 ) + return; + + assert(_level > 0); + + if ( --_level != 0 ) + return; + + assert(_time_started != Time()); + _time_used += (Clock::now() - _time_started); + _time_started = Time(); + ++_num_completed; + } + + void finish() { + if ( _level > 0 ) { + _time_used += (Clock::now() - _time_started); + _time_started = Time(); + ++_num_completed; + } + + _level = -1; + } + + Duration _time_used = Duration(0); + uint64_t _num_completed = 0; + uint64_t _level = 0; + std::string _name; + +private: + std::shared_ptr _manager; + Time _time_started; +}; + +/** Measure a code block's execution during its life-time. */ +class Collector { +public: + Collector(Ledger* ledger) : _ledger(ledger) { ledger->start(); } + + Collector(const std::string& name) { + _ledger = detail::Manager::singleton()->newLedger(name); + _ledger->start(); + } + + ~Collector() { _ledger->stop(); } + + void finish() { _ledger->finish(); } + + Collector() = delete; + Collector(const Collector&) = delete; + Collector(Collector&&) noexcept = delete; + Collector& operator=(const Collector&) = delete; + Collector& operator=(Collector&&) noexcept = delete; + +protected: + Ledger* _ledger; +}; + +} // namespace timing + +} // namespace util diff --git a/hilti/include/base/type_erase.h b/hilti/include/base/type_erase.h new file mode 100644 index 000000000..b31e6154b --- /dev/null +++ b/hilti/include/base/type_erase.h @@ -0,0 +1,276 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// Machinery for creating type-erased interface classes with value semantics. +// Needs help through an external Python script generating a bunch of boiler +// plate code. + +#pragma once + +#include + +#include + +#include + +namespace util::type_erasure { + +// If this defined, we track the number of type-erased instances by their +// actual type, and then print out a summary of the top types at the end as +// part of the timing summary. +// +// #define HILTI_TYPE_ERASURE_PROFILE + +namespace trait { +class TypeErased {}; +class Singleton {}; +} // namespace trait + +namespace detail { + +#ifdef HILTI_TYPE_ERASURE_PROFILE +struct Counters { + int64_t max = 0; + int64_t current = 0; + + void increment() { + ++max; + ++current; + } + void decrement() { --current; } +}; + +inline auto& instance_counters() { + static std::unordered_map global_counters; + return global_counters; +} +#endif + +} // namespace detail + +extern void summary(std::ostream& out); + +/** Internal base class defining the type-erased interface. */ +class ConceptBase { +public: + virtual const std::type_info& typeid_() const = 0; + virtual std::string typename_() const = 0; + virtual uintptr_t identity() const = 0; // Returns unique identity of current value + + // For internal use only. + virtual std::pair _childAs(const std::type_info& ti) const = 0; + virtual std::pair _childAs(const std::type_info& ti) = 0; +}; + +/** Internal base class for implementation of type-erased concept. */ +template +class ModelBase : public Concept { +public: + ModelBase(T data, ConceptArgs&&... args) : Concept(std::forward(args)...), _data(std::move(data)) { +#ifdef HILTI_TYPE_ERASURE_PROFILE + detail::instance_counters()[typeid(T).name()].increment(); +#endif + } + + ~ModelBase() override { +#ifdef HILTI_TYPE_ERASURE_PROFILE + detail::instance_counters()[typeid(T).name()].decrement(); +#endif + } + + ModelBase() = delete; + ModelBase(const ModelBase&) = default; + ModelBase(ModelBase&&) = default; + ModelBase& operator=(const ModelBase&) = default; + ModelBase& operator=(ModelBase&&) = default; + + + const T& data() const { return this->_data; } + T& data() { return _data; } + + uintptr_t identity() const final { + if constexpr ( std::is_base_of::value ) + return _data.data()->identity(); // NOLINT + + return reinterpret_cast(&_data); + } + + const std::type_info& typeid_() const final { return typeid(T); } + + std::string typename_() const final { + // Get the inner name if we store a type erased type in turn. + if constexpr ( std::is_base_of::value ) + return data().typename_(); // NOLINT + + return util::typename_(); + } + + std::pair _childAs(const std::type_info& ti) const final { + const ConceptBase* base = nullptr; + + if constexpr ( std::is_base_of::value ) + base = _data.data().get(); //NOLINT + + if ( typeid(_data) == ti ) + return {base, &_data}; + + return {base, nullptr}; + } + + std::pair _childAs(const std::type_info& ti) final { + ConceptBase* base = nullptr; + + if constexpr ( std::is_base_of::value ) + base = _data.data().get(); //NOLINT + + if ( typeid(_data) == ti ) + return {base, &_data}; + + return {base, nullptr}; + } + +private: + T _data; +}; + +/** Base class for the publicly visible, type-erased interface class. */ +template typename Model, typename... ConceptArgs> +class ErasedBase : public trait::TypeErased { +public: + ErasedBase() = default; + ErasedBase(const ErasedBase& other) = default; + ErasedBase(ErasedBase&& other) noexcept = default; + ErasedBase& operator=(const ErasedBase& other) = default; + ErasedBase& operator=(ErasedBase&& other) noexcept = default; + virtual ~ErasedBase() = default; // Make class polymorphic + + template + ErasedBase(T t, ConceptArgs&&... args) + : _data(std::make_shared>(std::move(t), std::forward(args)...)) {} + + explicit ErasedBase(std::shared_ptr data) : _data(std::move(data)) {} + ErasedBase& operator=(std::shared_ptr data) { + _data = std::move(data); + ; + return *this; + } + + /** + * Returns type information for the contained type. If multiple + * type-erased objects are nested, it will return the information for the + * inner-most type. + */ + const std::type_info& typeid_() const { + assert(_data); + return _data->typeid_(); + } + + /** + * Returns C++ type name for the contained type. If multiple type-erased + * objects are nested, it will return the information for the inner-most + * type. + */ + std::string typename_() const { return _data ? _data->typename_() : ""; } + + /** + * Casts the contained object into a specified type. This will aborts + * execution if the cast is not possible. + */ + template + const T& as() const { + if ( auto p = _tryAs() ) + return *p; + + std::cerr << util::fmt("internal error: unexpected type, want %s but have %s", util::typename_(), + typename_()) + << std::endl; + util::abort_with_backtrace(); + } + + /** + * Casts the contained object into a specified type. This will aborts + * execution if the cast is not possible. + */ + template + T& as() { + if ( auto p = _tryAs() ) + return *p; + + std::cerr << util::fmt("internal error: unexpected type, want %s but have %s", util::typename_(), + typename_()) + << std::endl; + util::abort_with_backtrace(); + } + + /** + * Returns true if the contained object can be casted into a specified + * type. + */ + template + bool isA() const { + return _tryAs() != nullptr; + } + + /** Attempts to cast the contained object into a specified type. */ + template + std::optional tryAs() const { + if ( auto p = _tryAs() ) + return *p; + + return {}; + } + + /** For internal use. */ + auto& data() const { return _data; } + + /** For internal use. */ + auto& data() { return _data; } + + /** For internal use. */ + uintptr_t identity() const { return _data ? _data->identity() : 0; } + +private: + template + const T* _tryAs() const { + if constexpr ( std::is_base_of::value ) + return static_cast(this); + + if ( typeid(Model) == typeid(*_data) ) + return &(std::static_pointer_cast>(_data))->data(); + + std::pair c = {_data.get(), nullptr}; + + while ( c.first ) { + c = c.first->_childAs(typeid(T)); + + if ( c.second ) + return static_cast(c.second); + } + + return nullptr; + } + + template + T* _tryAs() { + if constexpr ( std::is_base_of::value ) + return static_cast(this); + + if ( typeid(Model) == typeid(*_data) ) + return &(std::static_pointer_cast>(_data))->data(); + + std::pair c = {_data.get(), nullptr}; + + while ( c.first ) { + c = c.first->_childAs(typeid(T)); + + if ( c.second ) + return static_cast(c.second); + } + + return nullptr; + } + + // See https://stackoverflow.com/questions/18709647/shared-pointer-to-an-immutable-type-has-value-semantics + std::shared_ptr _data; +}; + +} // namespace util::type_erasure diff --git a/hilti/include/base/uniquer.h b/hilti/include/base/uniquer.h new file mode 100644 index 000000000..c27acf623 --- /dev/null +++ b/hilti/include/base/uniquer.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace util { + +/** + * Specialized cache that makes IDs unique, based on previously created ones. + * The *ID* type must allow assignment from string to set its value. + */ +template +class Uniquer : private Cache { +public: + /** + * If we see *id* for the 1st time, returns it (potentially normalized). + * Otherwise returns a modified version that's guaranteed to not have + * been returned before. + * + * @param normalize If true, always modifies the returned ID to be a + * valid C ID. + */ + ID get(ID name, bool normalize = true) { + if ( normalize ) + name = util::toIdentifier(name); + + auto x = name; + int i = 1; + while ( true ) { + if ( ! this->has(x) ) { + this->put(x, true); + return x; + } + + x = util::fmt("%s_%d", name, ++i); + } + } + + /** Clears a previously returned name for reuse. */ + void remove(const ID& id) { this->Cache::remove(id); } +}; + +} // namespace util diff --git a/hilti/include/base/util.h b/hilti/include/base/util.h new file mode 100644 index 000000000..69452e642 --- /dev/null +++ b/hilti/include/base/util.h @@ -0,0 +1,648 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace util::detail { +/** Helper that forwards to `Logger`. */ +void __internal_error(const std::string& s); +} // namespace util::detail + +#undef TINYFORMAT_ERROR +#define TINYFORMAT_ERROR(reason) ::util::detail::__internal_error(reason) +#include +#include +#include +#include + +/** + * Helper macro to mark variables that are intentionally unused. This + * silences the compiler warning. From + * http://stackoverflow.com/questions/777261/avoiding-unused-variables-warnings-when-using-assert-in-a-release-build + */ +#define _UNUSED(x) ((void)(x)); + +/** Tests if class is derived from another. */ +#define IF_DERIVED_FROM(t, cls) typename std::enable_if_t::value>* = nullptr + +/** Tests if class is not derived from another. */ +#define IF_NOT_DERIVED_FROM(t, cls) typename std::enable_if_t::value>* = nullptr + +/** Tests if two are class are the same. */ +#define IF_SAME(t, cls) typename std::enable_if_t::value>* = nullptr + +/** Tests if two are class are not the same. */ +#define IF_NOT_SAME(t, cls) typename std::enable_if_t::value>* = nullptr + +namespace util { + +/** Wrapper around the ABI's C++ demangle function. */ +using hilti::rt::demangle; + +/** Aborts with an internal error saying we should not be where we are. */ +extern void cannot_be_reached() __attribute__((noreturn)); + +/** Returns a type's demangled C++ name. */ +template +std::string typename_() { + return demangle(typeid(T).name()); +} + +/** sprintf-style string formatting. */ +template +std::string fmt(const char* fmt, const Args&... args) { + return tfm::format(fmt, args...); +} + +/** Applies a function to each element of a vector. */ +template +auto transform(const std::vector& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + y.emplace_back(f(i)); + return y; +} + +/** Applies a function to each element of a set. */ +template +auto transform(const std::set& x, F f) { + using Y = typename std::result_of::type; + std::set y; + for ( const auto& i : x ) + y.insert(f(i)); + return y; +} + +/** Applies a function to each element of an initializer_list. */ +template +auto transform(const std::initializer_list& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + for ( const auto& i : x ) + y.emplace_back(f(i)); + return y; +} + +/** Applies a function to each element of a set, returning a vector with the results. */ +template +auto transform_to_vector(const std::set& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + y.push_back(f(i)); + return y; +} + +/** Filters a vector through a boolean predicate. */ +template +auto filter(const std::vector& x, F f) { + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + if ( f(i) ) + y.emplace_back(i); + return y; +} + +/** Filters a set through a boolean predicate. */ +template +auto filter(const std::set& x, F f) { + std::set y; + for ( const auto& i : x ) + if ( f(i) ) + y.insert(i); + return y; +} + +/** + * Python-style enumerate() that returns an iterable yielding pairs `(index, + * val)`. From http://reedbeta.com/blog/python-like-enumerate-in-cpp17/. + */ +template())), + typename = decltype(std::end(std::declval()))> +constexpr auto enumerate(T&& iterable) { + struct iterator { + size_t i; + TIter iter; + bool operator!=(const iterator& other) const { return iter != other.iter; } + void operator++() { + ++i; + ++iter; + } + auto operator*() const { return std::tie(i, *iter); } + }; + struct iterable_wrapper { + T iterable; + auto begin() { return iterator{0, std::begin(iterable)}; } + auto end() { return iterator{0, std::end(iterable)}; } + }; + return iterable_wrapper{std::forward(iterable)}; +} + +/** Splits a string at all occurrences of a delimiter. */ +extern std::vector split(std::string s, const std::string& delim = " "); + +/** + * Splits a string once at the 1st occurrence of a delimiter. Leaves the 2nd + * element of the result pair unset if the delimiter does not occur. + */ +extern std::pair split1(std::string s, const std::string& delim = " "); + +/** + * Splits a string once at the last occurrence of a delimiter. Leaves the 1st + * element of the result pair unset if the delimiter does not occur. + */ +extern std::pair rsplit1(std::string s, const std::string& delim = " "); + +/** + * Returns a subrange of a vector, specified through zero-based indices. If + * indices are out of range, they are cut back to start/end of input. + * + * @param v vector to slice + * @param begin 1st index; if negative, counts from end Python-style + * @param end one beyond last index; if negative, counts from end Python-style + */ +template +std::vector slice(const std::vector& v, unsigned int begin, unsigned int end = -1) { + if ( begin < 0 ) + begin = v.size() + begin; + + if ( begin > v.size() ) + return {}; + + if ( end < 0 ) + end = v.size() + end + 1; + + if ( begin < 0 ) + begin = 0; + + if ( end < 0 ) + end = 0; + + if ( end > v.size() ) + end = v.size(); + + return std::vector(v.begin() + begin, v.begin() + end); +} + +/** + * Joins elements of a vector into a string, using a given delimiter to + * separate then. + */ +template +std::string join(const T& l, const std::string& delim = "") { + std::string result; + bool first = true; + + for ( const auto& i : l ) { + if ( not first ) + result += delim; + result += std::string(i); + first = false; + } + + return result; +} + +/** + * Joins elements of an initializer list into a string, using a given + * delimiter to separate then. + */ +template +std::string join(const std::initializer_list& l, const std::string& delim = "") { + std::string result; + bool first = true; + + for ( const auto& i : l ) { + if ( not first ) + result += delim; + result += std::string(i); + first = false; + } + + return result; +} + +/** + * Joins elements of an iterable range into a string, using a given delimiter + * to separate then. + */ +template +std::string join(const iterator& begin, const iterator& end, const std::string& delim = "") { + std::string result; + bool first = true; + + for ( iterator i = begin; i != end; i++ ) { + if ( not first ) + result += delim; + result += std::string(*i); + first = false; + } + + return result; +} + + +/** + * Splits a string into white-space-delimited pieces, prefixes each piece + * with another string, and then joins it all back together. + * + * Optionally filters out strings with a specific tag: If an inclusion tag is + * specified, each string is inspected if it starts with ``!!``. If it + * does, it's only included if ``tag == include_tag``. Strings without tags + * are always included. + * + * \note This is primarily a helper for creating our configuration files from + * CMake input. + + * @param in string to split + * @param prefix prefix to add to each part + * @param include_tag filter tags + * @return reassembled string with parts prefixed + */ +extern std::string prefixParts(const std::string& in, const std::string& prefix, const std::string& include_tag = ""); + +/** + * For each string in a vector, splits them into white-space delimited + * pieces, then joins all pieces into a single new vector of strings. + * + * \note This is primarily a helper for creating our configuration files from + * CMake input. + * + * @param in vector with strings to each splits + * @return reassembled vector + */ +extern std::vector flattenParts(const std::vector& in); + +/** Replaces all occurrences of one string with another. */ +extern std::string replace(const std::string& s, const std::string& o, const std::string& n); + +/** Returns a lower-case version of a string. */ +extern std::string tolower(const std::string& s); + +/** Returns a upper-case version of a string. */ +extern std::string toupper(const std::string& s); + +/** Returns a string with all leading & trailing white space removed. */ +extern std::string trim(const std::string& s); + +/** Returns a string with all trailing white space removed. */ +extern std::string rtrim(const std::string& s); + +/** Returns a string with all leading white space removed. */ +extern std::string ltrim(const std::string& s); + +/** Returns true if a string begins with another. */ +inline bool startsWith(const std::string& s, const std::string& prefix) { return s.find(prefix) == 0; } + +/** Returns true if a string ends with another. */ +extern bool endsWith(const std::string& s, const std::string& suffix); + +/** Returns a simple (non-crypto) hash value of a std::string. */ +extern uint64_t hash(const std::string& str); + +/** Returns a simple (non-crypto) hash value of a memory block. */ +extern uint64_t hash(const char* data, size_t len); + +/** + * Returns the valid value range for a signed integer of a given width. + * Supports only standard widths 8/16/32/64. + */ +constexpr std::pair signed_integer_range(int width) { + switch ( width ) { + case 8: return std::make_pair(INT8_MIN, INT8_MAX); + case 16: return std::make_pair(INT16_MIN, INT16_MAX); + case 32: return std::make_pair(INT32_MIN, INT32_MAX); + case 64: return std::make_pair(INT64_MIN, INT64_MAX); + default: throw std::out_of_range("unsupported integer width"); + } +} + +/** + * Returns the valid value range for an unsigned integer of a given width. + * Supports only standard widths 8/16/32/64. + */ +constexpr std::pair unsigned_integer_range(int width) { + switch ( width ) { + case 8: return std::make_pair(0, UINT8_MAX); + case 16: return std::make_pair(0, UINT16_MAX); + case 32: return std::make_pair(0, UINT32_MAX); + case 64: return std::make_pair(0, UINT64_MAX); + default: throw std::out_of_range("unsupported integer width"); + } +} + +/** + * Converts digits to an unsigned integer relative to a given base. + * + * @param dgts: null-terminated chars: decimal digits, hexits or base-n-digits + * @param base: base to use {0,2,3,...,36} (base 0 auto-detects like strtoull). + * @param handler: an error-handling function object or lambda. + */ +template +uint64_t chars_to_uint64(const char* dgts, unsigned int base, Error handler) { + errno = 0; + char* cp; + auto u = strtoul(dgts, &cp, base); + if ( cp == dgts || *cp != '\0' || (u == ULONG_MAX && errno == ERANGE) ) { + errno = 0; + handler(); + } + return u; +}; + +/** + * Converts digits to double precision floating point. + * + * @param dgts: null-terminated chars: decimal floating-point or hexfloat format. + * @param handler: an error-handling function object or lambda. + */ +template +double chars_to_double(const char* dgts, Error handler) { + errno = 0; + char* cp; + auto d = strtod(dgts, &cp); + if ( cp == dgts || *cp != '\0' || (d == HUGE_VAL && errno == ERANGE) ) { + errno = 0; + handler(); + } + + return d; +}; + +/** + * Converts an integer into a string relative to a given base. + * + * @param value: value to convert + * @param base: base to use + * @param n: The maximum number of characters to include. If the final string would + * be longer than this, it's cut off. If smaller than zero, includes all. + * + * @return converted string + */ +extern std::string uitoa_n(uint64_t value, unsigned int base, int n = -1); + +using hilti::rt::escapeBytes; +using hilti::rt::escapeUTF8; +using hilti::rt::expandEscapes; + +/** + * Turns an arbitrary string into something that can be used as C-level + * identifier. + * + * @param s string to convert. + * @param ensure_non_keyword if true, the returned ID will be expanded to make + * sure it won't accidentally match a compiler keyword. + * @return valid C identifier + */ +extern std::string toIdentifier(const std::string& s, bool ensure_non_keyword = false); + +/** Returns the current time in seconds since the epoch. */ +extern double currentTime(); + +/** Search a file name inside a given set of paths. */ +extern hilti::Result findInPaths(const std::filesystem::path& file, + const std::vector& paths); + +/** Turns a path into an absolute path with all dots removed. */ +std::filesystem::path normalizePath(const std::filesystem::path& p); + +/** + * Creates a temporary file in the system temporary directory. + * + * @param prefix prefix to use for the file's basename + * @return a valid path or an error + * */ +hilti::Result createTemporaryFile(const std::string& prefix = ""); + +/** Returns the path of the current executable. */ +std::filesystem::path currentExecutable(); + +/** Dumps a backtrace to stderr and then aborts execution. */ +[[noreturn]] extern void abort_with_backtrace(); + +/** Parses an string into an integer value. */ +template +inline auto atoi_n(Iter s, Iter e, int base, Result* result) { + return hilti::rt::atoi_n(s, e, base, result); +} + +/** + * Pairs up the elements of two lists. + * + * From http://stackoverflow.com/questions/10420380/c-zip-variadic-templates. + */ +template +std::list> zip2(const std::list& lhs, const std::list& rhs) { + std::list> result; + for ( std::pair::const_iterator, typename std::list::const_iterator> iter = + std::pair::const_iterator, typename std::list::const_iterator>(lhs.cbegin(), + rhs.cbegin()); + iter.first != lhs.end() and iter.second != rhs.end(); ++iter.first, ++iter.second ) + result.emplace_back(*iter.first, *iter.second); + return result; +} + +/** + * Pairs up the elements of two vectors. + * + * From http://stackoverflow.com/questions/10420380/c-zip-variadic-templates. + */ +template +std::vector> zip2(const std::vector& lhs, const std::vector& rhs) { + std::vector> result; + for ( std::pair::const_iterator, typename std::vector::const_iterator> iter = + std::pair::const_iterator, typename std::vector::const_iterator>(lhs.cbegin(), + rhs.cbegin()); + iter.first != lhs.end() and iter.second != rhs.end(); ++iter.first, ++iter.second ) + result.emplace_back(*iter.first, *iter.second); + return result; +} + +/** Returns the keys of a map as a set. */ +template +std::set map_keys(const std::map& m) { + std::set l; + + for ( const auto& i : m ) + l.insert(i.first); + + return l; +} + +/** Returns the values of a map as a set. */ +template +std::set map_values(const std::map& m) { + std::set l; + + for ( const auto& i : m ) + l.insert(i.second); + + return l; +} + +/** Returns the keys of a map as a set. */ +template +std::set map_keys(const std::unordered_map& m) { + std::set l; + + for ( const auto& i : m ) + l.insert(i.first); + + return l; +} + +/** Returns the values of a map as a set. */ +template +std::set map_values(const std::unordered_map& m) { + std::set l; + + for ( const auto& i : m ) + l.insert(i.second); + + return l; +} + +/** Returns the difference of two sets. This is a convenience wrapper around std::set_difference. */ +template> +std::set set_difference(const std::set& a, const std::set& b) { + std::set r; + std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(r, r.end()), Compare()); + return r; +} + +/** Returns the intersection of two sets. This is a convenience wrapper around std::set_intersection. */ +template> +std::set set_intersection(std::set& a, std::set& b) { + std::set r; + std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::inserter(r, r.end()), Compare()); + return r; +} + +/** Returns the union of two sets. This is a convenience wrapper around std::set_union. */ +template> +std::set set_union(const std::set& a, const std::set& b) { + std::set r; + std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::inserter(r, r.end()), Compare()); + return r; +} + +/** Concatenates two vectors into a new one. */ +template +std::vector concat(std::vector v1, const std::vector& v2) { + v1.reserve(v1.size() + v2.size()); + v1.insert(v1.end(), v2.begin(), v2.end()); + return v1; +} + +/** Appends a vector to another one. */ +template +std::vector& append(std::vector& v1, const std::vector& v2) { + v1.reserve(v1.size() + v2.size()); + v1.insert(v1.end(), v2.begin(), v2.end()); + return v1; +} + +/** + * Given an associative container and an index hint, returns a new index + * value that doesn't exist in the container yet. If the hint itself doesn't + * exist yet, it's returned directly. + */ +template +std::string uniqueIndex(const T& c, std::string hint) { + if ( c.find(hint) == c.end() ) + return hint; + + std::string idx; + int cnt = 1; + + while ( true ) { + std::string idx = fmt("%s.%d", hint, ++cnt); + if ( c.find(idx) == c.end() ) + return idx; + } +} + +/** Copies the content of one stream into another one. Returns true if successful. */ +inline bool copyStream(std::istream& in, std::ostream& out) { + char buffer[4096]; + while ( in.good() ) { + in.read(buffer, sizeof(buffer)); + out.write(buffer, sizeof(buffer)); + } + + return in.eof(); +} + +namespace enum_ { + +/** Helper class mapping an enum value to a string label. */ +template +struct Value { + E value; + const char* name; +}; + +/** + * Converts a string label to an enumerator value, based on a mapping table. + * + * @tparam Enum enum type that the mapping operation applies to + * @tparam Size number of enumerators that the enum type has + * @param name name to convert into enumerator + * @param values array of enumerator-to-string mappings + * + * @throws `std::out_of_range` if *name* is not found in *values* + */ +template +constexpr auto from_string(const std::string_view name, const Value (&values)[Size]) { + for ( const auto& v : values ) + if ( v.name == name ) + return v.value; + + throw std::out_of_range(name.data()); +}; + +/** + * Converts an enumerator value to string label, based on a mapping table. + * + * @tparam Enum enum type that the mapping operation applies to + * @tparam Size number of enumerators the enum type has + * @param value enumerator to convert into string + * @param values array of enumerator-to-string mappings + * + * @throws `std::out_of_range` if *value* is not found in *values* + */ +template +constexpr auto to_string(Enum value, const Value (&values)[Size]) { + for ( const auto& v : values ) + if ( v.value == value ) + return v.name; + + throw std::out_of_range(std::to_string(int(value))); +}; + +} // namespace enum_ + +} // namespace util diff --git a/hilti/include/base/visitor-types.h b/hilti/include/base/visitor-types.h new file mode 100644 index 000000000..900246849 --- /dev/null +++ b/hilti/include/base/visitor-types.h @@ -0,0 +1,72 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace hilti::visitor { + +/** Represents the location of a single node inside an AST during iteration. */ +template +class Location { +public: + E operator*() const { return node; } + typename std::remove_reference::type* operator->() const { return &node; } + + // private: // TODO: friend doesn't work? + // friend class Iterator; + Location(E node = nullptr, int child = 0) : node(node), child(child) {} + E node; + int child; +}; + +/** Represents the path to a node inside an AST during iteration. */ +template +struct Position { +public: + using Erased = typename std::decay::type; + + /** Node the position refers to. */ + E node; + + /** + * Path to reach the node. The node itself is the last element inside the + * path. + */ + const std::vector>& path; + + /** + * Returns the length of the AST path to the current node if we're indeed + * traversing an AST. If we're just dispatching a single node, this will + * return zero. + */ + auto pathLength() const { return path.size(); } + + /** + * Returns a parent. + * + * @param parent_nr number of the parent to return; 1 returns immediate parent, 2 the 2nd, etc. + * @exception `std::out_of_range` if the requested parent does not exist + */ + E parent(unsigned int parent_nr = 1) const { // 1st parent == 1 + if ( path.size() < 1 + parent_nr ) + throw std::out_of_range("node does not have requested parent"); + + return (**(path.end() - 1 - parent_nr)); + } + + /** Returns the first parent that has a given type. */ + template + std::optional> findParent() const { + for ( auto i = path.rbegin() + 1; i != path.rend(); i++ ) { + if ( (**i).template isA() ) + return {(**i).template as()}; + } + + return std::nullopt; + } +}; + +} // namespace hilti::visitor diff --git a/hilti/include/base/visitor-util.h b/hilti/include/base/visitor-util.h new file mode 100644 index 000000000..5a0371ff1 --- /dev/null +++ b/hilti/include/base/visitor-util.h @@ -0,0 +1,88 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +namespace hilti { +namespace visitor { + +/** + * Represents a path inside an AST from the root node to a node reached + * during iteration. + */ +template +using Path = std::vector>; + +/** Given an AST path, returns the current node. */ +template +N& current(const Path& path) { + if ( path.empty() ) + logger().internalError("empty path in visitor"); + + return (*(path.end() - 1)).get(); +} + +/** + * Given an AST path, returns a parent to the current node. + * + * @param path AST path + * @param parent_nr number of the parent to return; 1 returns immediate parent, 2 the 2nd, etc. + * @exception `std::out_of_range` if the requested parent does not exist + */ +inline const Node& parent(const Path& path, int parent_nr = 1) { + if ( path.size() < 1 + parent_nr ) + throw std::out_of_range("node does not have requested parent"); + + return (*(path.end() - 1 - parent_nr)).get(); +} + +/** + * Given an AST path, returns a parent to the current node. + * + * @param path AST path + * @param parent_nr number of the parent to return; 1 returns immediate parent, 2 the 2nd, etc. + * @exception `std::out_of_range` if the requested parent does not exist + */ +inline Node& parent(const Path& path, int parent_nr = 1) { + if ( path.size() < 1 + parent_nr ) + throw std::out_of_range("node does not have requested parent"); + + return (*(path.end() - 1 - parent_nr)).get(); +} + +/** + * Given an AST path, return the first parent of the current node that has a + * given type. + */ +template +std::optional findParent(const Path& path) { + for ( auto i = path.rbegin() + 1; i != path.rend(); i++ ) { + if ( auto t = (*i).get().tryAs() ) + return std::move(t); + } + + return {}; +} + +/** + * Given an AST path, return the first parent of the current node that has a + * given type. + */ +template +std::optional findParent(const Path& path) { + for ( auto i = path.rbegin() + 1; i != path.rend(); i++ ) { + if ( auto t = (*i).get().tryAs() ) + return std::move(t); + } + + return {}; +} + + +} // namespace visitor +} // namespace hilti diff --git a/hilti/include/base/visitor.h b/hilti/include/base/visitor.h new file mode 100644 index 000000000..7e350ef48 --- /dev/null +++ b/hilti/include/base/visitor.h @@ -0,0 +1,346 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +namespace hilti { +namespace detail::visitor { + +enum class Order { Pre, Post }; + +// hasCallback +// Test if Visitor class C has specific operator() 'callback' type +// (uses detection idiom SFINAE; in C++20 just use a requires clause) +// +template +struct hasCallback : std::false_type {}; + +template // remove_cv used as type_identity +struct hasCallback{&C::operator()})>> : std::true_type { +}; + +template +inline constexpr bool has_callback = (hasCallback::value || ...); + +template +using DispatchResult = std::conditional_t, bool, std::optional>; + +template +DispatchResult do_dispatch(Erased& n, Dispatcher& d, typename Iterator::Position& i, // NOLINT + bool& no_match_so_far); // NOLINT + +template +DispatchResult do_dispatch_one(Erased& n, const std::type_info& ti, Dispatcher& d, + typename Iterator::Position& i, bool& no_match_so_far) { // NOLINT + if ( ti != typeid(Type) ) + return {}; + + using T = std::conditional_t, const Type, Type>; + + using CBc = Result(T const&); + using CBcIP = Result(T const&, typename Iterator::Position); + + auto& x = n.template as(); + DispatchResult result = {}; + + // Prefer most specific callback, so climb down first. + if constexpr ( std::is_base_of_v ) + result = do_dispatch(x, d, i, no_match_so_far); + + if constexpr ( std::is_void_v ) { + // No result expected, call all matching methods. + (void)result; + if constexpr ( has_callback ) { + no_match_so_far = false; + d(x); + } + + if constexpr ( has_callback ) { + no_match_so_far = false; + d(x, i); + } + + return false; // Continue matching. + } + + else { // NOLINT + // Single result expected, stop at first matching method. + if ( result ) + return result; + + if constexpr ( has_callback ) { + no_match_so_far = false; + return {d(x)}; + } + + if constexpr ( has_callback ) { + no_match_so_far = false; + return {d(x, i)}; + } + } + + return {}; +} + +template +DispatchResult do_dispatch(Erased& n, Dispatcher& d, typename Iterator::Position& i, // NOLINT + bool& no_match_so_far) { // NOLINT + auto& tn = n.typeid_(); + +#ifdef VISITOR_DISPATCHERS + VISITOR_DISPATCHERS +#else +#error "VISITOR_DISPATCHERS not defined, did you include 'autogen/dispatchers.h'?" +#endif + + if constexpr ( std::is_void_v ) + return ! no_match_so_far; + else // NOLINT + return std::nullopt; +} + +/////////////// + +template +class Iterator { +public: + using E = std::conditional_t; + using Location = ::hilti::visitor::Location; + using Position = ::hilti::visitor::Position; + + Iterator() = default; + Iterator(E root) { _path.emplace_back(root, -1); } + + Iterator(const Iterator& other) = default; + Iterator(Iterator&& other) noexcept = default; + + ~Iterator() = default; + + Iterator& operator++() { + next(); + return *this; + } + Position operator*() const { return current(); } + + Iterator& operator=(const Iterator& other) = default; + Iterator& operator=(Iterator&& other) noexcept = default; + bool operator==(const Iterator& other) const { return _path.empty() && other._path.empty(); } + + bool operator!=(const Iterator& other) const { return ! (*this == other); } + +private: + void next() { + if ( _path.empty() ) + return; + + auto& p = _path.back(); + p.child += 1; + + if ( p.child == -1 ) { + if constexpr ( order == Order::Pre ) + return; + + next(); + return; + } + + assert(p.child >= 0); + + if ( p.child < static_cast(p.node.childs().size()) ) { + _path.emplace_back(p.node.childs()[p.child], -2); + next(); + return; + } + + if ( p.child == static_cast(p.node.childs().size()) ) { + if constexpr ( order == Order::Post ) + return; + + p.child += 1; + } + + if ( p.child > static_cast(p.node.childs().size()) ) { + _path.pop_back(); + next(); + return; + } + } + + Position current() const { + if ( _path.empty() ) + throw std::runtime_error("invalid reference of visitor's iterator"); + + auto& p = _path.back(); + + if ( p.child < 0 ) // pre order + return Position{.node = p.node, .path = _path}; + + if ( p.child == static_cast(p.node.childs().size()) ) // post order + return Position{.node = p.node, .path = _path}; + + assert(p.child < static_cast(p.node.childs().size())); + return Position{.node = p.node.childs()[p.child], .path = _path}; + } + + std::vector _path; +}; + +template +class ConstView { +public: + using iterator_t = typename Visitor::const_iterator_t; + + ConstView(const typename Visitor::erased_t& root) : _root(root) {} + + auto begin() { + if constexpr ( Visitor::order_ == Order::Pre ) + return iterator_t(_root); + + return ++iterator_t(_root); + } + + auto end() { return iterator_t(); } + +private: + const typename Visitor::erased_t& _root; +}; + +template +class NonConstView { +public: + using iterator_t = typename Visitor::iterator_t; + + NonConstView(typename Visitor::erased_t& root) : _root(root) {} + + auto begin() { + if constexpr ( Visitor::order_ == Order::Pre ) + return iterator_t(_root); + + return ++iterator_t(_root); + } + + auto end() { return iterator_t(); } + +private: + typename Visitor::erased_t& _root; +}; + +/** + * AST visitor. + * + * @tparam Result type the dispatch methods (and hence the visitor) returns + * @tparam Dispatcher class definining dispatch methods + * @tparam Erased type-erased class to dispatch on + * @tparam order order of iteration + */ +template +class Visitor { +public: + using result_t = Result; + using erased_t = Erased; + using base_t = Visitor; + using visitor_t = Dispatcher; + using iterator_t = Iterator; + using const_iterator_t = Iterator; + using position_t = typename iterator_t::Position; + using const_position_t = typename const_iterator_t::Position; + static const Order order_ = order; + + Visitor() = default; + + virtual void preDispatch(const Erased& /* n */, int /* level */){}; + + /** Execute matching dispatch methods for a single node. */ + auto dispatch(position_t& i) { + bool no_match_so_far = true; + preDispatch(i.node, i.pathLength()); + return do_dispatch(i.node, *static_cast(this), i, + no_match_so_far); + } + + /** Execute matching dispatch methods for a single node. */ + auto dispatch(const_position_t& i) { + bool no_match_so_far = true; + preDispatch(i.node, i.pathLength()); + return do_dispatch(i.node, *static_cast(this), + i, no_match_so_far); + } + + /** + * Execute matching dispatch methods for a single node. + * + * This method takes just the node itself and operates as it were the + * root of an AST. + */ + auto dispatch(Erased* n) { + bool no_match_so_far = true; + std::vector path; + position_t i = {*n, path}; + preDispatch(*n, 0); + return do_dispatch(*n, *static_cast(this), i, + no_match_so_far); + } + + /** + * Execute matching dispatch methods for a single node. + * + * This method takes just the node itself and operates as it were the + * root of an AST. + */ + auto dispatch(const Erased& n) { + Erased n_ = n; + bool no_match_so_far = true; + std::vector path; + position_t i = {n_, path}; + preDispatch(n_, 0); + return do_dispatch(n_, *static_cast(this), i, + no_match_so_far); + } + + /** + * Iterate over AST and Execute matching dispatch methods for each node. + * + * This method operates on a constant AST, and the dispatcher cannot + * modify any nodes. + * + * @note The returned view operates on referneces to the the AST passed + * in, so make sure that stays around as long as necessary. + */ + auto walk(const Erased& root) { return ConstView(root); } + + /** + * Iterate over AST and Execute matching dispatch methods for each node. + * + * This method operates on a non-constant AST, and the dispatcher may + * modify nodes. + * + * @note The returned view operates on referneces to the the AST passed + * in, so make sure that stays around as long as necessary. + */ + auto walk(Erased* root) { return NonConstView(*root); } +}; + +using NoDispatcher = struct {}; + +} // namespace detail::visitor + +/** + * Visitor performing a pre-order iteration over an AST. + */ +namespace visitor { +template +using PreOrder = detail::visitor::Visitor; + +/** + * Visitor performing a post-order iteration over an AST. + */ +template +using PostOrder = detail::visitor::Visitor; + +} // namespace visitor + +} // namespace hilti diff --git a/hilti/include/compiler/coercion.h b/hilti/include/compiler/coercion.h new file mode 100644 index 000000000..e64470105 --- /dev/null +++ b/hilti/include/compiler/coercion.h @@ -0,0 +1,273 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include + +namespace hilti { + +/** Tunes the specifics of a type coercion operation. */ +enum class CoercionStyle { + /** + * Specifies that coercion is taking place in the context of a assignment + * of the source expression to a variable of the destination type. + */ + Assignment = (1U << 0U), + + /** + * Specifies that coercion is taking place in the context of matching the + * source expression against a target operand during operator resolution. + */ + OperandMatching = (1U << 1U), + + /** + * Specifies that coercion is taking place in the context of passing the + * source expression to a function parameter of the target type. + */ + FunctionCall = (1U << 2U), + + /** + * Let coercion succeed if the types fully match. (You probably always + * want this). + */ + TryExactMatch = (1U << 3U), + + /** + * Let coercion succeed if the source type can be converted into the + * destination type by a legal constness change. + */ + TryConstPromotion = (1U << 4U), + + /** + * Let coercion succeed if the source type can be converted into the + * destination type by any of the plugins' provided type coercions. (This + * is the main path to performing actual coercions that change types.) + */ + TryCoercion = (1U << 5U), + + /** + * If the source expression's AST node has an original type associated + * with it, use that's type for coersion. + */ + PreferOriginalType = (1U << 6U), + + /** Never allow any substantial type changes. */ + DisallowTypeChanges = (1U << 7U), + + /** + * Signal that the coercion takes place in a semantic language context + * expecting the given destination type. This can be used to support + * coercions at locations where normally it wouldn't take place, such as + * conversion to bool in conditional statements. + */ + ContextualConversion = (1U << 8U), + + /** Internal flag signaling the coercion code is recursing. */ + _Recursing = (1U << 10U), + + /** + * Shortcut style activating all possible coercions in the context of an + * assignment. + */ + TryAllForAssignment = (1U << 0U) | (1U << 3U) | (1U << 4U) | (1U << 5U) | (1U << 6U), + + /** + * Shortcut style activating all possible coercions in the context of + * operator resolution. + */ + TryAllForMatching = (1U << 1U) | (1U << 3U) | (1U << 4U) | (1U << 5U) | (1U << 6U), + + /** + * Shortcut style activating possible coercions in the context of + * function parameter passing, however without allowing any type changes. + */ + TryDirectMatchForFunctionCall = (1U << 2U) | (1U << 3U) | (1U << 4U) | (1U << 6U), + + /** + * Shortcut style activating all possible coercions in the context of + * function parameter passing. + */ + TryAllForFunctionCall = (1U << 2U) | (1U << 3U) | (1U << 4U) | (1U << 5U) | (1U << 6U), + + /** + * Shortcut style allowing for direct matches only in the context of + * operator resolution. + */ + TryDirectForMatching = (1U << 1U) | (1U << 3U) | (1U << 4U) | (1U << 6U) +}; + +/** + * Returns a readable represenation of a coercion style setting for debugging + * purposes. + */ +extern std::string to_string(bitmask style); + +} // namespace hilti + +enableEnumClassBitmask(hilti::CoercionStyle); // Must be in global scope + +namespace hilti { + +/** Return type for the functions doing expression coercion. */ +struct CoercedExpression { + /** Returns true if coercion was successful. */ + operator bool() const { return coerced; } + + /** + * Coerced expression if succesful, an error if not. This will be set + * even if the coerced expression ends up being identical to the source + * expression. + */ + Result coerced = {}; + + /** + * Coerced expression if succesful and the coerced expression is not + * identical to original one; unset otherwise. + */ + std::optional nexpr = {}; + + /** + * If coerced is set, true if type of new expression's type is to be + * considered changed compared to source expression's type for overload + * resolution + */ + bool consider_type_changed = false; + + /** + * + * Represents a successful coercion that led the source expression not + * changing, which will be assigned to the `coerced` field. + * + * @note The expression not changing doesn't necessarily mean that + * the expression's type is *exactly* matching the coercion's destination + * type. However, even if not, the caller should proceed by using the + * `coerced` field value for anywhere where the coerced expression is + * expected. + * + * @param src the original source expression + */ + CoercedExpression(const Expression& src) : coerced(src) {} + + /** + * Represents a successful coercion that led to a new expression + * different from the source expression. + * + * @param src the original source expression's type + * @param coerced the resulting expression that *src* was coerced to + */ + CoercedExpression(Type src, Expression coerced) + : coerced(coerced), + nexpr(coerced), + consider_type_changed(type::effectiveType(std::move(src)).typename_() != + type::effectiveType(coerced.type()).typename_()) {} + + /** Represents an unsuccessful coercion. */ + CoercedExpression() = default; + + /** + * Represents an unsuccessful coercion, carrying an error message along + * explaining why it failed. + */ + CoercedExpression(const result::Error& error) : coerced(error) {} +}; + +/** + * Coerces an expression to a given target type. This returns a struct with + * fields that provide result of the coercion, along with additional meta + * information. Depending on the coercion style, a coerced expression may be + * the exact same expression as passed in if types match sufficiently. + * + * @note This function does not actually *perform* the coercion, it just + * returns an AST of the specified target type that will let the compiler + * later carry out the coercion (usually that'll be a `expression::Coerced`` + * node). + * + * @param e expression to coerce + * @param dst target type + * @param style coercion style to use, given as a bitmask of any style + * specifiers that apply + * @return the *result* will evaluate to true if coercion was successful; if + * so, the contained fields will provide more information + */ +CoercedExpression coerceExpression(const Expression& e, const Type& dst, + bitmask style = CoercionStyle::TryAllForAssignment); + +/** + * Coerces an expression to a given target type. This returns a struct with + * fields that provide result of the coercion, along with additional meta + * information. Depending on the coercion style, a coerced expression may be + * the exact same expression as passed in if types match sufficiently. + * + * @note This function does not actually *perform* the coercion, it just + * returns an AST of the specified target type that will let the compiler + * later carry out the coercion (usually that'll be a `expression::Coerced`` + * node). + * + * @param e expression to coerce + * @param src explicitly specified source type; this can be different from + * the type of *e* and will be used instead of that + * @param dst target type + * @param style coercion style to use, given as a bitmask of any style + * specifiers that apply + * @return the *result* will evaluate to true if coercion was successful; if + * so, the contained fields will provide more information + */ +CoercedExpression coerceExpression(const Expression& e, const Type& src_, const Type& dst_, + bitmask style = CoercionStyle::TryAllForAssignment); + +/** + * Matches a set of expressions against a set of operands, coercing them as + * needed. This takes into account specifics of the operands, such as them + * being optional or having defaults. + * + * @param exprs source expressions to match against the operands + * @param operands operands to match against + * @param style coercion style to use for each expression's coercion to its + * operand, given as a bitmask of any style + * specifiers that apply + * @return If successful, a pair with a boolean as its 1st argument that + * indicates whether any of the expressions was changed; and as its 2nd + * element, the coerced expressions now matching the operands. The returned + * vector will have defaults filled in for missing expressions where + * available (missing expressions for optional operands without defaults will + * remain left out). If unsuccessful, an error. + */ +Result>> coerceOperands(const std::vector& exprs, + const std::vector& operands, + bitmask style); + +/** + * Coerces a constructor to a given target type. This returns the coerced + * constructor, now of the new type. If the constructor is already of the + * right type, it will just be returned back. + * + * @param c ctor to coerce + * @param dst target type + * @param style coercion style to use + * @return if the coercion was successful, the returned new value (which may be the same as the old) + */ +Result coerceCtor(Ctor c, const Type& dst, bitmask style = CoercionStyle::TryAllForAssignment); + +/** + * Coerces a source type to a given target type. This returns the coerced + * type. If the type is already of the right type, it will just be returned + * back. + * + * @param c ctor to coerce + * @param dst target type + * @param style coercion style to use + * @return if the coercion was successful, the returned new value (which may be the same as the old) + */ +Result coerceType(const Type& src_, const Type& dst_, + bitmask style = CoercionStyle::TryAllForAssignment); + + +} // namespace hilti diff --git a/hilti/include/compiler/context.h b/hilti/include/compiler/context.h new file mode 100644 index 000000000..cbf8d26cd --- /dev/null +++ b/hilti/include/compiler/context.h @@ -0,0 +1,155 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hilti { + +class PluginRegistry; + +/** Options controlling the compiler's code generation */ +struct Options { + bool debug = false; /**< if true, generate non-optimized debug code */ + bool debug_trace = + false; /**< if true, generate code to log statements to debug stream "hilti-trace" (requires *debug*, too) */ + bool debug_flow = false; /**< if true, generate code to log function calls and returns to debug stream "hilti-flow" + (requires *debug*, too) */ + bool debug_location = false; /**< if true, generate code to record current source code location during execution + (requires *debug*, too) */ + bool skip_validation = false; /**< if true, skip AST validation; for debugging only, things will may downhiull + quickly if an AST is not well-formed */ + bool optimize = false; /**< generated optimized code */ + std::vector library_paths; /**< additional directories to search for imported files */ + std::string cxx_namespace_extern = + "hlt"; /**< CXX namespace for generated C++ code accessible to the host application */ + std::string cxx_namespace_intern = "__hlt"; /**< CXX namespace for generated internal C++ code */ + std::vector + cxx_include_paths; /**< additional C++ directories to search for #include files. */ + + /** + * Parses a comma-separated list of tokens indicating which additional + * debug instrumentation to activate, and sets the instance's + * corresponding options. + * + * @return An error if a flag isn't known. + */ + Result parseDebugAddl(const std::string& flags); +}; + +namespace context { + +/** + * Index into the context's cache of already proceesed modules. Note that we + * use ID and path interchangeably, a module can be accessed by *either*, + * meaning that the mapping from path to ID must be consisten throughout all + * processing. + */ +struct ModuleIndex { + ID id; /**< module ID */ + std::filesystem::path path; /**< path to module's source code on disk; can be left empty if no file exists */ + + ModuleIndex() = default; + ModuleIndex(ID id, const std::filesystem::path& path) : id(std::move(id)), path(util::normalizePath(path)) {} + bool operator<(const ModuleIndex& other) const { return id < other.id; } +}; + +/** + * Caches information about an already processed module. Note that these are + * "living" objects that keep being updated during AST processing. Only once + * "final" is set, the information is assumed to correct and no longer + * changing. + */ +struct CachedModule { + ModuleIndex index; /**< ID and path of module */ + NodeRef node; /**< module's root AST node */ + bool requires_compilation = + false; /**< true if the module contains code that requires compilation itself (vs. modules that only declare + elements, but don't generate produce any code for linking) */ + std::optional> dependencies; /**< further modules imported by the processed one */ + + bool final = false; /**< once true, one can start relying on the other fields outside of AST processing */ + + CachedModule() = default; + CachedModule(ModuleIndex index, NodeRef node) : index(std::move(index)), node(std::move(node)) {} +}; + +} // namespace context + +/** Context storing compiler-wide state */ +class Context { +public: + /** + * @param options options to use for code compilation + */ + explicit Context(Options options); + + /** Returns the context's compiler options. */ + const Options& options() const { return _options; } + + /** + * Makes a new module known to the context, which will take ownershiup + * and cache it, along with further meta data. A module with the same ID + * or path must only be registered once, the method will abort otherwise. + * + * @param idx cache index for module + * @param module module to cache + * @param requires_compilation initial value for the corresponding `CachedModule` field; this may later be + * overridden if AST processing finds out more + * @return the meta data associated with the newly registered module + */ + const context::CachedModule& registerModule(const context::ModuleIndex& idx, Node&& module, + bool requires_compilation); + + /** + * Updates the meta data associated with a previoysly cached module AST. + * + * @param module module to cache; all the fields of the struct must have been filled out + */ + void updateModule(const context::CachedModule& module); + + /** + * Looks up a previously cached module AST. + * + * @param id ID that was used to cache the AST + * @return the meta data associated with the previously cached module, or not set if no module is associated with + * that ID + */ + std::optional lookupModule(const ID& id); + + /** + * Looks up a previously cached module AST. + * + * @param path path that was used to cache the AST + * @return the meta data associated with the previously cached module, or not set if no module is associated with + * that path + */ + std::optional lookupModule(const std::filesystem::path& path); + + /** + * Returns all (direct) dependencies that a modulee imports. This + * information may be correct yet, if `final` isn't set in the module + * meta data. + * + * @param meta data for all dependencies + */ + std::vector lookupDependenciesForModule(const ID& id); + +private: + Options _options; + + std::vector, std::shared_ptr>> _modules; + std::unordered_map> _module_cache_by_id; + std::unordered_map> _module_cache_by_path; +}; + +} // namespace hilti diff --git a/hilti/include/compiler/detail/clang.h b/hilti/include/compiler/detail/clang.h new file mode 100644 index 000000000..e3aa4e7f7 --- /dev/null +++ b/hilti/include/compiler/detail/clang.h @@ -0,0 +1,117 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#ifndef HILTI_HAVE_JIT +#error clang.h cannot be included if JIT support is not compiled in +#endif + +namespace hilti::detail { + +/** JIT implementation using clang/LLVM as the backend. */ +class ClangJIT { +public: + /** + * Constructor + * + * @param context global context to pull settings from + */ + ClangJIT(std::shared_ptr context); + ~ClangJIT(); + + ClangJIT(const ClangJIT&) = delete; + ClangJIT(ClangJIT&&) noexcept = delete; + ClangJIT& operator=(const ClangJIT&) = delete; + ClangJIT& operator=(ClangJIT&&) noexcept = delete; + + /** + * Compiles one C++ module into LLVM bitcode. This kicks off Clang + * compilation and then stores the resulting LLVM module internally. for + * later linking. + * + * This must be called after ``init()`` and before ``jit()``. + * + * @param code in-memory representation of the C++ code to compile + * @return true if compilation succeeded; the LLVM module will then have + * been recorded internally for later linking + */ + bool compile(const CxxCode& code); + + /** + * Compiles one C++ module into LLVM bitcode. This kicks off Clang + * compilation and then stores the resulting LLVM module internally. for + * later linking. + * + * This must be called after ``init()`` and before ``jit()``. + * + * @param p path to read C++ code from + * @return true if compilation succeeded; the LLVM module will then have + * been recorded internally for later linking + */ + bool compile(const std::filesystem::path& p); + + /* + * Links all LLVM< bitcode modules compiled far into one LLVM module + * using LLVM's linker class. Then, just in times that joined module and + * adds its symbols to the JIT. Like its cousin, `init()`, this method + * failing is likely reason to stop execution. + * + * This must be called after ``init()`` and after all desired code has + * been added. + * + * @return success if linking and JITing was successful, an appropiate + * error otherwise. + */ + Result jit(); + + /** + * Retrieves the compiled object code. This must be called only after + * ``jit()`` has succeeded and will return the shared library for the + * final fully-linked module. + */ + std::optional> retrieveLibrary() const; + + /** + * Activates saving any emitted code to disk for debugging purposes. + * It will land in files ``dbg.*``. + */ + void setDumpCode(); + + /** + * Prepares the runtime environment for execution. In particular runs + * any static constructors. + * + * This must be called only after ``jit()`` has succeeded. + * + * @return true if initialization was succesfull + */ + bool initRuntime(); + + /** + * Cleans up the runtime environment after execution. In particular runs + * any static destructors. + * + * This must be called only after ``initRuntime()`` has succeeded. + * Afterwards no more functionality of this class may be used. + * + * @return true if clean up was succesfull + */ + bool finishRuntime(); + + /** Returns a string describing the version of Clang compiler in use. */ + static std::string compilerVersion(); + +private: + struct Implementation; + std::unique_ptr _impl; // PIMPL +}; + +} // namespace hilti::detail diff --git a/hilti/include/compiler/detail/codegen/codegen.h b/hilti/include/compiler/detail/codegen/codegen.h new file mode 100644 index 000000000..e5d1bb7bd --- /dev/null +++ b/hilti/include/compiler/detail/codegen/codegen.h @@ -0,0 +1,109 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace hilti { + +class Node; +class Unit; + +namespace detail { + +namespace codegen { +enum class TypeUsage { Storage, CopyParameter, InParameter, InOutParameter, FunctionResult, Ctor, None }; + +struct CxxTypes { + std::optional base_type; + std::optional storage; + std::optional result; + std::optional param_copy; + std::optional param_in; + std::optional param_inout; + std::optional ctor; + std::optional default_; +}; +} // namespace codegen + +/** + * HILTI's code generator. This is the main internal entry point for + * generating C++ code from HILTI source code. + */ +class CodeGen { +public: + CodeGen(std::shared_ptr context) : _context(std::move(context)) {} + + /** Entry point for code generation. */ + Result compileModule(Node& root, hilti::Unit* hilti_unit); // NOLINT(google-runtime-references) + + /** Entry point for generating additional cross-unit C++ code through HILTI's linker. */ + Result linkUnits(const std::vector& mds); + + std::shared_ptr context() const { return _context; } + const Options& options() const { return _context->options(); } + + // These must be called only while a module is being compiled. + std::optional typeDeclaration(const hilti::Type& t); + std::list typeDependencies(const hilti::Type& t); + cxx::Type compile(const hilti::Type& t, codegen::TypeUsage usage); + cxx::Expression compile(const hilti::Expression& e, bool lhs = false); + cxx::Expression compile(const hilti::Ctor& c); + cxx::Expression compile(const hilti::expression::ResolvedOperator& o, bool lhs = false); + cxx::Block compile(const hilti::Statement& s, cxx::Block* b = nullptr); + cxx::declaration::Function compile(const ID& id, type::Function ft, declaration::Linkage linkage, + function::CallingConvention cc = function::CallingConvention::Standard, + const std::optional& fattrs = {}, + std::optional namespace_ = {}); + std::vector compileCallArguments(const std::vector& args, + const std::vector& params); + std::optional typeDefaultValue(const hilti::Type& t); + cxx::Expression coerce(const cxx::Expression& e, const Type& src, const Type& dst); // only for supported coercions + cxx::Expression unpack(const hilti::Type& t, const Expression& data, const std::vector& args); + cxx::Expression unpack(const hilti::Type& t, const cxx::Expression& data, const std::vector& args); + void addDeclarationFor(const hilti::Type& t) { _need_decls.push_back(t); } + + cxx::Expression addTmp(const std::string& prefix, const cxx::Type& t); + cxx::Expression addTmp(const std::string& prefix, const cxx::Expression& init); + + cxx::Expression self() const { return _selfs.back(); } + cxx::Expression dollardollar() const { + return "__dd"; + } // TODO(robin): We hardcode the currently; need a stack, too? + void pushSelf(detail::cxx::Expression e) { _selfs.push_back(std::move(e)); } + void popSelf() { _selfs.pop_back(); } + + auto cxxBlock() const { return ! _cxx_blocks.empty() ? _cxx_blocks.back() : nullptr; } + void pushCxxBlock(cxx::Block* b) { _cxx_blocks.push_back(b); } + void popCxxBlock() { _cxx_blocks.pop_back(); } + + void enablePrioritizeTypes() { ++_prioritize_types; } + void disablePrioritizeTypes() { --_prioritize_types; } + bool prioritizeTypes() const { return _prioritize_types > 0; } + + cxx::Unit* unit() const; // will abort if not compiling a module. + hilti::Unit* hiltiUnit() const; // will abort if not compiling a module. + +private: + std::unique_ptr _cxx_unit; + hilti::Unit* _hilti_unit = nullptr; + std::shared_ptr _context; + std::vector _selfs = {"__self"}; + std::vector _cxx_blocks; + std::vector _tmps; + std::map _tmp_counters; + std::vector _need_decls; + util::Cache _cache_types_storage; + util::Cache _cache_types_declarations; + int _prioritize_types = 0; +}; + +} // namespace detail +} // namespace hilti diff --git a/hilti/include/compiler/detail/cxx/all.h b/hilti/include/compiler/detail/cxx/all.h new file mode 100644 index 000000000..c852de7c6 --- /dev/null +++ b/hilti/include/compiler/detail/cxx/all.h @@ -0,0 +1,7 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include diff --git a/hilti/include/compiler/detail/cxx/elements.h b/hilti/include/compiler/detail/cxx/elements.h new file mode 100644 index 000000000..7eb756576 --- /dev/null +++ b/hilti/include/compiler/detail/cxx/elements.h @@ -0,0 +1,337 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hilti::detail::cxx { + +class Formatter; + +namespace element { +enum class Type { Expression, Type, Linkage, Attribute }; +} // namespace element + +/** An element of type `T` in the compiler's intemediary C++ representation. */ +template +class Element { +public: + Element() = default; + Element(std::string s) : _s(std::move(s)) {} + Element(const char* s) : _s(s) {} + + Element& operator=(const std::string& s) { + _s = s; + return *this; + } + Element& operator=(std::string&& s) { + _s = std::move(s); + return *this; + } + Element& operator=(const char* s) { + _s = s; + return *this; + } + + bool isMultiLine() const { return _s.find('\n') != std::string::npos; } + + operator std::string() const { return _s; } + explicit operator bool() const { return ! _s.empty(); } + bool operator<(const Element& s) const { return _s < s._s; } + bool operator==(const Element& s) const { return _s == s._s; } + +private: + std::string _s; +}; + +using Attribute = Element; /**< C++ function attribute */ +using Expression = Element; /**< C++ expression */ +using Linkage = Element; /**< C++ linkage specification */ +using Type = Element; /**< C++ type */ + +extern std::string normalize_id(std::string id); + +/** A C++ ID. */ +class ID : public detail::IDBase { +public: + using Base = detail::IDBase; + using Base::IDBase; + ID() = default; + explicit ID(const ::hilti::ID& id) : Base(std::string(id)) {} + ID& operator=(const ::hilti::ID& id) { + *this = ID(id); + return *this; + } + + /** Wrapper to construct an ID from an already normalized string name. */ + static ID fromNormalized(std::string id) { return ID(std::move(id), Base::AlreadyNormalized()); } + +private: + static std::string _normalize(std::string id); +}; + +extern void to_json(nlohmann::json& j, const cxx::ID& id); // NOLINT +extern void from_json(const nlohmann::json& j, cxx::ID& id); // NOLINT + +namespace declaration { + +/** A C++ `@include` specific ation. */ +struct IncludeFile { + std::string file; + bool operator<(const IncludeFile& o) const { return file < o.file; } +}; + +/** Declaration of a local C++ variable. */ +struct Local { + cxx::ID id; + cxx::Type type; + std::vector args; + std::optional init; + Linkage linkage; + + std::string str() const; + operator std::string() const { return str(); } +}; + +/** Declaration of a global C++ variable. */ +struct Global { + cxx::ID id; + cxx::Type type; + std::vector args; + std::optional init; + Linkage linkage; + + bool operator==(const Global& other) const { + return id == other.id && type == other.type && init == other.init && linkage == other.linkage; + } + + std::string str() const; + operator std::string() const { return str(); } +}; + +/** Declaration of a C++ constant. */ +struct Constant { + cxx::ID id; + cxx::Type type = Type(); + std::optional init; + Linkage linkage; + bool operator<(const Constant& s) const { return id < s.id; } + + bool operator==(const Constant& other) const { + return id == other.id && type == other.type && init == other.init && linkage == other.linkage; + } +}; + +extern void to_json(nlohmann::json& j, const Constant& c); // NOLINT +extern void from_json(const nlohmann::json& j, Constant& c); // NOLINT + +/** Declaration of a C++ type. */ +struct Type { + cxx::ID id; + cxx::Type type; + std::string inline_code = ""; + bool forward_decl = false; + bool forward_decl_prio = false; + bool no_using = false; + + bool operator==(const Type& other) const { + return id == other.id && type == other.type && inline_code == other.inline_code && + forward_decl == other.forward_decl && forward_decl_prio == other.forward_decl_prio && + no_using == other.no_using; + } +}; + +extern void to_json(nlohmann::json& j, const Type& t); // NOLINT +extern void from_json(const nlohmann::json& j, Type& t); // NOLINT + +/** Declaration of a C++ function argument. */ +struct Argument { + cxx::ID id; + cxx::Type type; + cxx::Type internal_type = ""; + operator std::string() const { return id ? util::fmt("%s %s", type, id) : std::string(type); } + + bool operator==(const Argument& other) const { return type == other.type && id == other.id; } +}; + +extern void to_json(nlohmann::json& j, const Argument& a); // NOLINT +extern void from_json(const nlohmann::json& j, Argument& a); // NOLINT + +} // namespace declaration + +/** A C++ statement block. */ +class Block { +public: + Block() {} + Block(std::vector stmts); + + void addStatement(std::string stmt); + void addStatementAtFront(std::string stmt); + void addBlock(Block child); + void addComment(const std::string& stmt, bool sep_before = true, bool sep_after = false); + void addLocal(const declaration::Local& v); + void addTmp(const declaration::Local& v); + void addReturn(const Expression& expr = Expression()); + void addIf(const Expression& cond, Block true_); + void addIf(const Expression& init, const Expression& cond, cxx::Block true_); + void addIf(const Expression& cond, Block true_, Block false_); + void addIf(const Expression& init, const Expression& cond, Block true_, Block false_); + void addElseIf(const Expression& cond, Block true_); + void addElse(Block true_); + void addFor(const Expression& init, const Expression& cond, const Expression& next, const cxx::Block& body); + void addForRange(bool const_, const ID& id, const Expression& seq, const cxx::Block& body); + // void addForRange(const Expression& init, bool const_, const ID& id, const Expression& seq, cxx::Block body); // + // C++20 ... + void addWhile(const Expression& cond, const Block& body); + void addLambda(const std::string& name, const std::string& signature, Block body); + void addSwitch(const Expression& cond, const std::vector>& cases_, + std::optional default_ = {}); + void appendFromBlock(Block b); + void addTry(Block body, std::vector> catches); + + bool ensureBracesForBlock() const { return _ensure_braces_for_block; } + void setEnsureBracesforBlock() { _ensure_braces_for_block = true; } + + size_t size(bool ignore_comments = false) const; + + Block& operator+=(const Block& other); + + operator bool() const { return ! _stmts.empty(); } + + friend ::hilti::detail::cxx::Formatter& operator<<(Formatter& f, const Block& x); + + bool operator==(const Block& other) const { return _stmts == other._stmts; } + +private: + using Flags = unsigned int; + std::vector> _stmts; + std::vector _tmps; + bool _ensure_braces_for_block = false; +}; + +namespace declaration { + +/** Declaration of a C++ function. */ +struct Function { + cxx::Type result; + cxx::ID id; + std::vector args; + bool const_ = false; + Linkage linkage = "static"; + Attribute attribute = ""; + std::optional inline_body; // TODO(robin): Not serialized to JSON yet. + + std::string prototype(bool qualify) const; + std::string parameters() const; + + bool operator==(const Function& other) const { + return result == other.result && id == other.id && args == other.args && linkage == other.linkage && + attribute == other.attribute && inline_body == other.inline_body; + } +}; + +extern void to_json(nlohmann::json& j, const Function& f); // NOLINT +extern void from_json(const nlohmann::json& j, Function& f); // NOLINT + +} // namespace declaration + +/** A C++ function. */ +struct Function { + declaration::Function declaration; + Block body; + bool default_ = false; + + bool operator==(const Function& other) const { return declaration == other.declaration && body == other.body; } +}; + +namespace type { +namespace struct_ { + +using Member = std::variant; + +inline bool operator<(const Member& m1, const Member& m2) { + auto id = [](auto m) { + if ( auto x = std::get_if(&m) ) + return x->id; + if ( auto x = std::get_if(&m) ) + return x->id; + + throw std::bad_variant_access(); + }; + + return id(m1) < id(m2); +} + +} // namespace struct_ + +/** A C++ struct type. */ +struct Struct { + std::vector args; + std::vector members; + cxx::ID type_name; + std::optional self; + bool add_ctors = false; + std::string str() const; + std::string inlineCode() const; + + operator std::string() const { return str(); } + operator cxx::Type() const { return str(); } +}; + +namespace union_ { +using Member = struct_::Member; +} // namespace union_ + +/** A C++ union type. */ +struct Union { + std::vector members; + cxx::ID type_name; + std::string str() const; + operator std::string() const { return str(); } + operator cxx::Type() const { return str(); } +}; + +namespace enum_ { +using Label = std::pair; +} // namespace enum_ + +/** A C++ enum type. */ +struct Enum { + std::vector labels; + cxx::ID type_name; + std::string str() const; + operator std::string() const { return str(); } + operator cxx::Type() const { return str(); } +}; + +} // namespace type + +inline std::ostream& operator<<(std::ostream& o, const ID& i) { return o << std::string(i); } +inline std::ostream& operator<<(std::ostream& o, const Linkage& l) { return o << std::string(l); } +inline std::ostream& operator<<(std::ostream& o, const Type& t) { return o << std::string(t); } +inline std::ostream& operator<<(std::ostream& o, const Attribute& a) { return o << std::string(a); } +inline std::ostream& operator<<(std::ostream& o, const declaration::Argument& t) { return o << std::string(t); } +inline std::ostream& operator<<(std::ostream& o, const Expression& e) { return o << std::string(e); } + +extern Formatter& operator<<(Formatter& f, const Block& x); +extern Formatter& operator<<(Formatter& f, const Expression& x); +extern Formatter& operator<<(Formatter& f, const ID& x); +extern Formatter& operator<<(Formatter& f, const Function& x); +extern Formatter& operator<<(Formatter& f, const Type& x); +extern Formatter& operator<<(Formatter& f, const declaration::Type& x); +extern Formatter& operator<<(Formatter& f, const declaration::IncludeFile& x); +extern Formatter& operator<<(Formatter& f, const declaration::Local& x); +extern Formatter& operator<<(Formatter& f, const declaration::Global& x); +extern Formatter& operator<<(Formatter& f, const declaration::Function& x); +extern Formatter& operator<<(Formatter& f, const declaration::Constant& x); + +} // namespace hilti::detail::cxx diff --git a/hilti/include/compiler/detail/cxx/formatter.h b/hilti/include/compiler/detail/cxx/formatter.h new file mode 100644 index 000000000..501dd5271 --- /dev/null +++ b/hilti/include/compiler/detail/cxx/formatter.h @@ -0,0 +1,105 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti::detail::cxx { + +class Unit; + +/** Formatter for generating C++ code. */ +class Formatter : public CodeFormatter { +public: + /** + * Opens a new namespace that's relative to whatever the current + * namespace is. The new namespace will go onto the formatter's namespace + * stack. + * + * @param relative_ns the namespace, which will be used as is for a new + * `namespace` directive. + */ + void pushNamespace(const std::string& relative_ns); + + /** Removes the most recently opened namespace from the stack. */ + void popNamespace(); + + /** + * Enters a namespace for subsequent elements. In contrast to + * `pushNamespace`, this takes an absolute namespace (i.e., from the root + * level) that the method might adopt based on what the formatter's + * current namespace is. For example, if the current namespace matches + * the new namespace, no `namespace` directive needs to be inserted at + * all. If the new namespace is a sub-namespace of the current one, the + * inserted `namespace` directive will include only the relative part. + * + * @param absolute_ns the namespace, which will be adapted before being + * used as part of a a `namespace` directive. + */ + void enterNamespace(const std::string& absolute_ns); + + /** Leaves all current namespaces, clearing out the stack. */ + void leaveNamespace(); + + /** + * Returns the formatter's current absolute namespace, optionally just to + * a given level. + * + * @param level max level to include, with 1 being the first. + */ + std::optional namespace_(int level = -1) const; + + /** + * Adjust an ID's scoping relative to a namespace. + * + * @param id with absolute scoping + * @param level depth of current namespace to consider + * @return id with scoping relative to *level* elements of the current + * namespacing path + * + */ + cxx::ID relativeID(const cxx::ID& id, int level) const; + + bool ensure_braces_for_block = true; + bool compact_block = true; + bool eos_after_block = false; + bool sep_after_block = true; + +private: + std::vector _namespaces; +}; + +// TODO(robin): Can we factor out these operators into code-formatter.h? +template +inline Formatter& operator<<(Formatter& f, const T& t) { + return t(f); +} + +template::value>* = nullptr> +inline Formatter& operator<<(Formatter& f, const T& t) { + f.next(); + f.stream() << t; + return f; +} + +inline Formatter& operator<<(Formatter& f, const std::string& s) { + f.printString(s); + return f; +} +inline Formatter& operator<<(Formatter& f, const char* s) { + f.printString(s); + return f; +} + +namespace formatter { +using dedent = hilti::code_formatter::dedent; +using eol = hilti::code_formatter::eol; +using eos = hilti::code_formatter::eos; +using indent = hilti::code_formatter::indent; +using separator = hilti::code_formatter::separator; +using quoted = hilti::code_formatter::quoted; +using comment = hilti::code_formatter::comment; +} // namespace formatter + +} // namespace hilti::detail::cxx diff --git a/hilti/include/compiler/detail/cxx/linker.h b/hilti/include/compiler/detail/cxx/linker.h new file mode 100644 index 000000000..6a0cc50bc --- /dev/null +++ b/hilti/include/compiler/detail/cxx/linker.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace hilti::detail::cxx { + +/** + * HILTI's linker. + * + * It's not *really* a linker, it's a component that adds additional C++ code + * requires knowledge across all compilation units. That knowledge is + * included with each compiled C++ code unit as JSON data inside comments. + * The linker extracts all this information and then generates an additional + * C++ code unit with corresponding globa code. + */ +class Linker { +public: + Linker(CodeGen* cg) : _codegen(cg) {} + + void add(const linker::MetaData& md); + void finalize(); + Result linkerUnit(); // only after finalize and at least one module + +private: + CodeGen* _codegen; + std::optional _linker_unit; + + std::set> _modules; + std::map> _joins; + std::set _globals; +}; + +} // namespace hilti::detail::cxx diff --git a/hilti/include/compiler/detail/cxx/unit.h b/hilti/include/compiler/detail/cxx/unit.h new file mode 100644 index 000000000..82897b916 --- /dev/null +++ b/hilti/include/compiler/detail/cxx/unit.h @@ -0,0 +1,141 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace detail { + +namespace cxx { + +class Linker; + +namespace linker { + +using MetaData = nlohmann::json; + +/** + * Function joined by the linker. + * + * The HILTI linker will generate a C++ function `` that calls all + * `callee` function registered for that ID. + */ +struct Join { + cxx::ID id; /**< name of externally visible function */ + cxx::declaration::Function callee; /**< callee function to execute through linker function */ + std::list + aux_types; /**< additional types the linker needs to declare for external prototype to work */ + int priority = 0; /**< Priority determing the order between callees; higher prioeriy calleed will be called first */ + bool declare_only = false; /**< only declare the joined C++ function, don't generate the implementation */ + + bool operator<(const Join& other) const { + return std::make_tuple(id, priority, callee.id) < std::make_tuple(other.id, other.priority, other.callee.id); + } +}; + +extern void to_json(nlohmann::json& j, const Join& x); // NOLINT +extern void from_json(const nlohmann::json& j, Join& x); // NOLINT + +} // namespace linker + +/** One C++ code unit. */ +class Unit { +public: + Unit(std::shared_ptr context); + + void setModule(const hilti::Module& m); + cxx::ID moduleID() const { return _module_id; } + + void setUsesGlobals() { _uses_globals = true; } + + void add(const declaration::IncludeFile& i, const Meta& m = Meta()); + void add(const declaration::Global& g, const Meta& m = Meta()); + void add(const declaration::Constant& c, const Meta& m = Meta()); + void add(const declaration::Type& t, const Meta& m = Meta()); + void add(const declaration::Function& f, const Meta& m = Meta()); + void add(const Function& f, const Meta& m = Meta()); + void add(const std::string& stmt, const Meta& m = Meta()); // add generic top-level item + void add(const linker::Join& f); + + // Prioritize type with given ID to be written out so that others + // depending on it will have it available. + void prioritizeType(const cxx::ID& id) { + if ( std::find(_types_in_order.begin(), _types_in_order.end(), id) == _types_in_order.end() ) + _types_in_order.push_back(id); + } + + bool hasDeclarationFor(const cxx::ID& id); + std::optional lookupType(const cxx::ID& id) const; + + void addComment(const std::string& comment); + void addInitialization(cxx::Block block) { _init_module.appendFromBlock(std::move(block)); } + + Result finalize(); + + Result print(std::ostream& out) const; // only after finalize + Result createPrototypes(std::ostream& out); // only after finalize + void importDeclarations(const Unit& other); // only after finalize + Result linkerMetaData() const; // only after finalize + cxx::ID cxxNamespace() const; + + std::shared_ptr context() const { return _context; } + + static std::pair> readLinkerMetaData(std::istream& input); + +protected: + friend class Linker; + Unit(std::shared_ptr context, cxx::ID module_id); + Unit(std::shared_ptr context, cxx::ID module_id, const std::string& cxx_code); + +private: + void _addHeader(Formatter& f); + void _addModuleInitFunction(); + + std::shared_ptr _context; + + cxx::ID _module_id; + std::filesystem::path _module_path; + bool _no_linker_meta_data = false; + bool _uses_globals = false; + + std::optional _cxx_code; + + std::vector _comments; + std::set _includes; + std::map _types; + std::vector _types_in_order; + std::map _types_forward; + std::map _globals; + std::map _constants; + std::multimap _function_declarations; + std::multimap _function_implementations; + std::vector _statements; + std::set _linker_joins; // set to keep sorted. + std::set _namespaces; // set to keep sorted. + std::set _ids; + + cxx::Block _init_module; + cxx::Block _init_globals; +}; + +} // namespace cxx +} // namespace detail +} // namespace hilti diff --git a/hilti/include/compiler/detail/parser/driver.h b/hilti/include/compiler/detail/parser/driver.h new file mode 100644 index 000000000..57703da6e --- /dev/null +++ b/hilti/include/compiler/detail/parser/driver.h @@ -0,0 +1,136 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#ifdef yylex +#undef yylex +// Work-around for bison messing up the function name by adding the local namespace. +#define yylex lex +#endif + +#include + +#include +#include + +#include +#include + +#undef YY_DECL +#define YY_DECL \ + hilti::detail::parser::Parser::token_type \ + hilti::detail::parser::Scanner::lex(hilti::detail::parser::Parser::semantic_type* yylval, \ + hilti::detail::parser::location* yylloc, \ + hilti::detail::parser::Driver* driver) + +#define YYSTYPE yystype_hilti + +#ifndef __FLEX_LEXER_H +#define yyFlexLexer HiltiFlexLexer +#include + +#undef yyFlexLexer +#endif + +/** Bison value type. */ +struct yystype_hilti { + bool bool_ = false; + double real = 0.0; + uint64_t uint = 0; + int64_t sint = 0; + std::string str; + + hilti::ID id; + hilti::Declaration declaration; + hilti::Type type; + hilti::Ctor ctor; + hilti::Expression expression; + hilti::Statement statement; + hilti::Attribute attribute; + hilti::Function function; + hilti::type::Flags type_flags; + + std::optional opt_expression; + std::optional opt_statement; + std::optional opt_attributes; + + hilti::declaration::Linkage linkage; + hilti::declaration::parameter::Kind function_parameter_kind; + hilti::function::CallingConvention function_calling_convention; + hilti::type::function::Parameter function_parameter; + hilti::type::function::Result function_result; + hilti::type::function::Flavor function_flavor; + hilti::statement::switch_::Case switch_case; + hilti::statement::try_::Catch try_catch; + + std::vector strings; + std::vector declarations; + std::vector expressions; + std::vector statements; + std::vector function_parameters; + std::vector switch_cases; + std::vector try_catches; + + std::pair tuple_type_elem; + std::vector> tuple_type_elems; + + hilti::type::struct_::Field struct_field; + hilti::ctor::struct_::Field struct_elem; + std::vector struct_fields; + std::vector struct_elems; + + hilti::type::union_::Field union_field; + std::vector union_fields; + + hilti::ctor::Map::Element map_elem; + std::vector map_elems; + + hilti::type::enum_::Label enum_label; + std::vector enum_labels; + + std::pair, std::vector> decls_and_stmts; +}; + +namespace hilti { + +namespace logging::debug { +inline const DebugStream Parser("parser"); +} // namespace logging::debug + +namespace detail { +namespace parser { + +class Parser; +class Scanner; + +/** Driver for flex/bison. */ +class Driver { +public: + Result parse(std::istream& in, const std::string& filename); + + Scanner* scanner() const { return _scanner; } + Parser* parser() const { return _parser; } + + // Methods for the parser. + + std::string* currentFile() { return &_filename; } + void error(const std::string& msg, const Meta& m); + void enablePatternMode(); + void disablePatternMode(); + void enableExpressionMode(); + void disableExpressionMode(); + void enableDottedIDMode(); + void disableDottedIDMode(); + void setDestinationModule(Module&& m) { _module = std::move(m); } + +private: + Module _module; + std::string _filename; + Parser* _parser = nullptr; + Scanner* _scanner = nullptr; + int _expression_mode = 0; +}; + +} // namespace parser +} // namespace detail +} // namespace hilti diff --git a/hilti/include/compiler/detail/parser/scanner.h b/hilti/include/compiler/detail/parser/scanner.h new file mode 100644 index 000000000..fb426f444 --- /dev/null +++ b/hilti/include/compiler/detail/parser/scanner.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// This borrows from https://idlebox.net/2007/flex-bison-cpp-example. + +#pragma once + +#include + +#include + +/** We compile with a source property to find this. */ +#include <__parser.h> + +namespace hilti::detail::parser { + +/** HILTI's Flex scanner. */ +class Scanner : public HiltiFlexLexer { +public: + Scanner(std::istream* yyin = nullptr, std::ostream* yyout = nullptr) : HiltiFlexLexer(yyin, yyout) {} + + hilti::detail::parser::Parser::token_type lex(hilti::detail::parser::Parser::semantic_type* yylval, + hilti::detail::parser::location* yylloc, + hilti::detail::parser::Driver* driver); + + void enablePatternMode(); + void disablePatternMode(); + void enableExpressionMode(); + void disableExpressionMode(); + void enableDottedIDMode(); + void disableDottedIDMode(); +}; + +} // namespace hilti::detail::parser diff --git a/hilti/include/compiler/detail/visitors.h b/hilti/include/compiler/detail/visitors.h new file mode 100644 index 000000000..b20cfd404 --- /dev/null +++ b/hilti/include/compiler/detail/visitors.h @@ -0,0 +1,107 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +class Unit; + +namespace printer { +class Stream; +} // namespace printer + +namespace detail { + +/**Performs imports for an AST. */ +std::set importModules(const Node& root, Unit* unit); + +/** + * Prints an AST as HILTI source code. This consults any installed plugin + * `print_ast` hooks. + */ +void printAST(const Node& root, std::ostream& out, bool compact = false); + +/** + * Prints an AST as HILTI source code. This consults any installed plugin + * `print_ast` hooks. + */ +void printAST(const Node& root, printer::Stream& stream); // NOLINT + +/** Returns a string with the prototype for an operator for display. */ +std::string renderOperatorPrototype(const expression::UnresolvedOperator& o); + +/** Returns a string with the prototype for an operator for display. */ +std::string renderOperatorPrototype(const expression::ResolvedOperator& o); + +/** Returns a string with an instantiated operator for display. */ +std::string renderOperatorInstance(const expression::UnresolvedOperator& o); + +/** Returns a string with an instantiated operator for display. */ +std::string renderOperatorInstance(const expression::ResolvedOperator& o); + +/** Prints a debug dump of a node, including its childrens. */ +void renderNode(const Node& n, std::ostream& out, bool include_scopes = false); +void renderNode(const Node& n, logging::DebugStream stream, bool include_scopes = false); + +/** + * Resets dynamically built state in an AST. Currently, this clears all the + * scopes and any errors. + */ +void resetNodes(Node* root); + +/** + * Clears any errors currentluy set in an AST. + */ +void clearErrors(Node* root); + +/** Returns the number of nodes in an AST that have an error flagged. */ +extern int64_t errorsInAST(const Node& n); + +/** + * Reports all errors already recorded in the AST through the logger. + * + * @note this may filter out some errors to to avoid the output becoming + * noisy, such as when one error has triggered a chain of other ones. + * + * @return number of errors reported + */ +int reportErrorsInAST(const Node& root, Unit* unit); + +/** + * Returns the number of nodes in an AST that are "unresolved". A node is + * unresolved if it's type is out of a small set of AST node types that the + * ID and operator resolvers replace. + */ +extern int64_t unresolvedInAST(const Node& n); + +/** Returns a hash of an AST that stays stable as long as no nodes change. */ +uint64_t hashAST(const Node& n); + +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +void buildScopes(const std::vector>& modules, Unit* unit); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +bool resolveIDs(Node* root, Unit* unit); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +bool resolveOperators(Node* root, Unit* unit); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +std::optional coerceCtor(Ctor c, const Type& dst, bitmask style); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +std::optional coerceType(Type t, const Type& dst, bitmask style); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +bool applyCoercions(Node* root, Unit* unit); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +void validateAST(const Node& root); + + +} // namespace detail +} // namespace hilti diff --git a/hilti/include/compiler/driver.h b/hilti/include/compiler/driver.h new file mode 100644 index 000000000..44df71abb --- /dev/null +++ b/hilti/include/compiler/driver.h @@ -0,0 +1,428 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace hilti { + +class JIT; +struct Options; + +namespace driver { + +/** Enum to specify type of dependencies to output. */ +enum class Dependencies { + None, /**< No output of dependencies. */ + All, /**< Output all other modules being depended on. */ + Code /**< Output other modules being depended if they require separate compilation of their own to produce code. */ +}; + +/** + * Options for the compiler driver + * + * @note Only one of the `output_*` can be used at any time. + */ +struct Options { + bool include_linker = false; /**< if true, perform custom HILTI linker phase */ + bool output_hilti = false; /**< render HILTI inputs back into HILTI source code */ + bool output_prototypes = false; /**< output C++ prototypes for generated code */ + bool output_cxx = false; /**< output generated C++ code */ + bool output_linker = false; /**< output generated HILTI linker C++ code */ + Dependencies output_dependencies = Dependencies::None; /**< output dependencies for compiled modules */ + bool execute_code = false; /**< compile code, and execute unless output_path is set */ + bool disable_jit = false; /**< whether we should forcibly disable JIT, used for testing */ + bool show_backtraces = false; /**< include backtraces when printing unhandled excepttions */ + bool abort_on_exceptions = false; /**< abort() instead of throwing HILTI exceptions */ + bool keep_tmps = false; /**< do not delete any temporary files created */ + bool skip_dependencies = false; /**< do not automatically compile dependencies during JIT */ + bool report_times = false; /**< Report break-down of driver's execution time. */ + bool dump_code = false; /**< Record all final HILTI and C++ code to disk for debugging. */ + std::vector + inputs; /**< files to compile; these will be automatically pulled in by ``Driver::run()`` */ + std::filesystem::path output_path; /**< file to store output in (default if empty is printing to stdout) */ + std::unique_ptr + logger; /**< `Logger` instances to use for diagnostics; set to a new logger by default by constructor */ + + Options() { logger = std::make_unique(); } +}; + +} // namespace driver + +/** + * Compiler driver. + * + * The driver is a high-level building block for writing command-line tools + * compiling HILTI source files (and more). `hiltic` is just a tiny wrapper + * around this class. + * + * Classes can drive from the driver to expand its functionality, including + * for adding support for additional types of source files (e.g., Spicy + * code). + * + */ +class Driver { +public: + /** + * @param name descriptive name for the tool using the driver, which will + * be used in usage and error messages. + * @param argv0 if given, the current exectuable, which will tune the + * path's that the global options insance returns + */ + explicit Driver(std::string name, const std::string_view& argv0 = ""); + virtual ~Driver(); + + Driver() = delete; + Driver(const Driver&) = delete; + Driver(Driver&&) noexcept = delete; + Driver& operator=(const Driver&) = delete; + Driver& operator=(Driver&&) noexcept = delete; + + /** + * Frontend for parsing command line options into `driver::Options` and + * `hilti::Options``. See the outout of `hiltic --help` for a list. + * + * `setDriverOptions()` and `setCompilerOptions()` provide alternative + * ways to set thhe options directly. + * + * @param argc,argv command line arguments to parse + * @return set if succesfull; otherwise the result provides an error message + */ + Result parseOptions(int argc, char** argv); + + /** + * Schedules a HILTI module for compilation. The unit will take ownership + * and compile the module once `compile()` is called. If module of the same ID or + * path has been added previously, this will have no effect. + * + * `hookNewASTPreCompilation()` hook will be called immediately for the + * new module. + * + * @param m HILTI module to schedule for compilation + * @param path path associated with the module, if any + * @return set if succesfull; otherwise the result provides an error message + */ + Result addInput(hilti::Module&& m, const std::filesystem::path& path = ""); + + /** + * Schedules a HILTI source file for compilation. The file will be parsed + * immediately, and then compiled later when `compile()` is called. If the + * same file/module has been added previously, this method will have no + * effect. + * + * `hookNewASTPreCompilation()` hook will be called immediately for the + * new module after it has been parsed. + * + * @param input source of HILTI module to compile + * @return set if succesfull; otherwise the result provides an error message + */ + Result addInput(const std::filesystem::path& path); + + /** Returns true if at least one input file has been added. */ + bool hasInputs() const { + return _pending_units.size() || _processed_units.size() || _processed_paths.size() || _libraries.size() || + _external_cxxs.size(); + } + + /** Returns the driver options currently in effect. */ + const auto& driverOptions() const { return _driver_options; } + + /** Returns the HILTI compiler options currently in effect. */ + const auto& hiltiOptions() const { return _compiler_options; } + + /** + * Sets the driver's options and arguments. + * + * @param options the options + */ + void setDriverOptions(driver::Options options); + + /** + * Sets HILTI's compiler options. + * + * @param options the options + */ + void setCompilerOptions(hilti::Options options); + + /** + * Initializes the compilation process. Must be called after options have been set, + * and before any inputs are added. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result initialize(); + + /** + * Loads, compiles, and links the source files. This must be called only + * after driver and compiler options have been set. Internally, it chains + * the various `*Modules()` methods. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result compile(); + + /** + * Returns the current HILTI context. Valid only once compilation has + * started, otherwise null. + */ + auto context() const { return _ctx; } + + /** + * Initializes HILT's runtime system to prepare for JIT execution of + * compiled code. This will already trigger execution of all + * module-specific initialization code (initialization of globals; + * module-level stsatements). The method must be called only after + * `compile()` has run already. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result initRuntime(); + + /** + * Executes the `hilti_main` entry function in compiled code. This must + * be called only after `initRuntime()` has run already. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result executeMain(); + + /** + * Shuts down HILT's runtime library after JIT execution has concluded, + * cleaning up resources. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result finishRuntime(); + + /** + * Compile and executes all source files. This is a convience wrapper + * around the stages of the process provided by other methods. It + * executes all of `compile()`, `initRuntime()`, `executeMain()`, and + * `finishRuntime()` in that order. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result run(); + + /** + * Returns a pointer the internal JIT instance. This will be available + * only after `compile()` and `initRuntime()` have executed. + * + * @return pointer to the JIT instance if available + */ + Result jit(); + +protected: + /** + * Prints a usage message to stderr. The message summarizes the options + * understood by `parseOptions()`. + */ + void usage(); + + /** + * Main work horse compiling all registered input files to C++ code. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result compileUnits(); + + /** + * Runs the HILTI-side linker on all available C++ code. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result linkUnits(); + + /** + * Writes out generated code if requested by driver options. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result outputUnits(); + + /** + * Jits all code compiled so far. This must be called only if the driver + * has been configured to run the JIT. + * + * @return set if succesfull; otherwise the result provides an error message + */ + Result jitUnits(); + + /** + * Helper function to create an `result::Error` with a message that + * includig driver name and, optionally, a file the error refers to. + * + * @param msg error message + * @param p file to associate with the error, emoty for none + * @return error with an appropiately set message + */ + result::Error error(std::string_view msg, const std::filesystem::path& p = ""); + + /** + * Helper function to augment an `result::Error` with a message that + * includig driver name and, optionally, a file the error refers to. + * + * @param msg error message + * @param p file to associate with the error, emoty for none + * @return error with an appropiately set message + */ + result::Error augmentError(const result::Error& err, const std::filesystem::path& p = ""); + + /** + * Helper function to open a file for writing. + * + * @param p output file + * @param binary true to open in binary mode + * @return set if succesful, or an appropiate error result + */ + Result openOutput(const std::filesystem::path& p, bool binary = false); + + /** + * Helper function to open a file for reading. + * + * @param in input stream to open with file with + * @param p input file + * @return set if succesful, or an appropiate error result + */ + Result openInput(std::ifstream& in, const std::filesystem::path& p); + + /** + * Helper function to write data into an output file. + * + * @param in stream to read data to write from + * @param p output file + * @return set if succesful, or an appropiate error result + */ + Result writeOutput(std::ifstream& in, const std::filesystem::path& p); + + /** + * Helper function to read data from an input file. + * + * @param p input file + * @return string stream with the file's data, or an appropiate error result + */ + Result readInput(const std::filesystem::path& p); + + /** + * Copies an input stream into a temporary file on disk + * + * @param in stream to read from + * @param name_hint a string to include into the temporary file's name + * @param extension extension for the temporary file's name + * @return the path to the temporary file, or an appropiate error result + */ + Result writeToTemp(std::ifstream& in, const std::string& name_hint, + const std::string& extension = "tmp"); + + /** Save a unit's final HILTI and C++ code to disk for debugging. */ + void dumpUnit(const Unit& unit); + + /** + * Prints an uncaught HILTI exception to stderr. + * + * @param e exception to print + */ + void printHiltiException(const hilti::rt::Exception& e); + + /** + * Hook for derived classes to execute custom code when a new source path + * is being added as an input file. + */ + virtual void hookAddInput(const std::filesystem::path& path) {} + + /** + * Hook for derived classes to execute custom code when a new AST module + * is being added as an input file. + */ + virtual void hookAddInput(const hilti::Module& m, const std::filesystem::path& path) {} + + /** + * Hook for derived classes to execute custom code when an HILTI AST has + * been loaded. This hook will run before the AST has been compiled (and + * hence it'll be fully unprocessed). + */ + virtual void hookNewASTPreCompilation(const ID& name, const std::optional& path, + const Node& root) {} + + /** + * Hook for derived classes to execute custom code when a HILTI AST has + * been finalized. This hook will run after the AST has been compiled + * (and hence it'll be fully processed). + */ + virtual void hookNewASTPostCompilation(const ID& name, const std::optional& path, + const Node& root) {} + + /** + * Hook for derived classes to execute custom code when all input files + * have been compiled to HILTI & Spicy code (but not yet linked). If the + * hook return an error that will abort all further processing. The hook + * may add further inputs files through the `add()` methods, which will + * then be compiled next. If so, this hook will execute again once all + * new inputs have likewise been compiled. + */ + virtual Result hookCompilationFinished() { return Nothing(); } + + /** + * Hook for derived classes to execute custom code when the HILTI runtime + * has been intialized. + */ + virtual void hookInitRuntime() {} + + /** + * Hook for derived classes to execute custom code just before the HILTI + * runtime is being shut down. + */ + virtual void hookFinishRuntime() {} + +private: + // Tracking the state of the compilation pipeline to catch out of order + // operation. + enum Stage { UNINITIALIZED, INITIALIZED, FINALIZED, LINKED, JITTED } _stage = UNINITIALIZED; + + void _addUnit(Unit unit); + Result _compileUnit(Unit unit); + + bool _requires_jit() const { return ! _pending_units.empty(); } + + /** + * Look up a symbol in the global namespace. + * + * @param symbol the symbol to look up + * @return either a valid, not-nil pointer to the symbol or an error + */ + static Result _symbol(const std::string& symbol); + + std::string _name; + driver::Options _driver_options; + hilti::Options _compiler_options; + + std::vector _pending_units; + + std::set _processed_units; + std::set _processed_paths; + + std::shared_ptr _ctx; // driver's compiler context + std::unique_ptr _jit; // driver's JIT instance + + std::vector _generated_cxxs; + std::vector _libraries; + std::vector _external_cxxs; + std::vector _mds; + std::vector _hlts; + + bool _runtime_initialized = false; // true once initRuntime() has succeeded + std::set _tmp_files; // all tmp files created, so that we can clean them up. +}; + +} // namespace hilti diff --git a/hilti/include/compiler/jit.h b/hilti/include/compiler/jit.h new file mode 100644 index 000000000..5456c295f --- /dev/null +++ b/hilti/include/compiler/jit.h @@ -0,0 +1,271 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include + +namespace hilti { + +namespace logging::debug { +inline const DebugStream Jit("jit"); +} // namespace logging::debug + +namespace detail { +class ClangJIT; +} // namespace detail + +/** Container for C++ code compiled from a HILTI source file */ +class CxxCode { +public: + /** + * Reads C++ code from a file. + * + * @param path file to read + */ + CxxCode(const std::filesystem::path& path) { load(path); } + + /** + * Reads C++ code from an input stream. + * + * @param id name to associate with the input for logging and error messages. + * @param code stream to read from + */ + CxxCode(const std::string& id, std::istream& code) { load(id, code); } + + /** + * Initializes code instance from in-memory compiler output. For internal use. + * + * @param u unit to initialize code instance from + */ + explicit CxxCode(const detail::cxx::Unit& u); + + + /** + * Saves C++ code into a file. + * + * @param p file to write to + * @return true if succesful + */ + bool save(const std::filesystem::path& p) const; + + /** + * Writes C++ code into an output stream. + * + * @param out stream to write to + * @return true if succesful + */ + bool save(std::ostream& out) const; + + /** Returns C++ code as a string. */ + auto code() const { return _code; } + + /** Returns true if this instance has been initialized with any C++ code. */ + auto isLoaded() const { return _code.has_value(); } + + /** + * Returns a name associated with the instance's C++ code. If the code + * has been read from a file, that's the path; otherwise the ID specifed + * when initialized. + */ + const std::string& id() const { return _id; } + +protected: + /** + * Loads C++ code from a file. + * + * @param path file to read from + * @return true if succesful + */ + bool load(const std::filesystem::path& path); + + /** + * Loads C++ code from an input stream. + * + * @param id name to associate with the input for logging and error messages. + * @param path stream to read from + * @return true if succesful + */ + bool load(const std::string& id, std::istream& in); + +private: + std::string _id; + std::optional _code; +}; + +/** + * Container for storing code compiled into a native shared library. + * + * This class loads the underlying library it wraps into its internal store on + * construction and subsequently does not depend on it anymore. + */ +class Library { +public: + Library(const std::filesystem::path& path); + ~Library(); + + // Since this library has exclusive ownership of some path it cannot be copied. + Library(const Library&) = delete; + Library& operator=(const Library&) = delete; + + Library(Library&&) = default; + Library& operator=(Library&&) = default; + + /** + * Load the library into the current process + * + * @return nothing or an error + * */ + Result open() const; + + /** + * Save this library under a different path. + * + * @parm path the path where this library should be stored + * @return nothing or an error + */ + Result save(const std::filesystem::path& path) const; + +private: + std::filesystem::path _path; // Absolute path to the physical file wrapped by this instance. +}; + +/** + * Just-in-time compiler. + * + * The class provides the entry point for compiling and executing C++ code + * just in time. + * + * @note The compiler can be used only if the global configuration indicates + * that HILTI has been compiled with JIT support. + * + * @todo The error handling in this class isn't great. Most methods just + * return booleans, and otherwise report through the globa `Logger`; should + * switch that to `Result<>`. Worse, the actual compilation/linking outpus + * diagnostics directly to stderr currently. + * + * @todo Our JITing doesn't support C++ code with global + * intialization/cleanup code currently (like global ctors/dtors). It would + * probably be tricky to add that. + */ +class JIT { +public: + /** + * @param context compiler context to use + */ + explicit JIT(std::shared_ptr context); + ~JIT(); + + JIT() = delete; + JIT(const JIT&) = delete; + JIT(JIT&&) noexcept = delete; + JIT& operator=(const JIT&) = delete; + JIT& operator=(JIT&&) noexcept = delete; + + /** + * Schedules C++ for just-in-time compilation. This must be called only + * before `compile()`. + * + * @param d C++ code + */ + void add(CxxCode d) { _codes.push_back(std::move(d)); } + + /** + * Adds a precompiled shared library. This must be called only before + * `jit()`. + * + * @param library precompiled shared library + */ + Result add(Library library) { return library.open(); } + + /** + * Activates saving any emitted code to disk for debugging purposes. + * It will land in files ``dbg.*``. + */ + void setDumpCode(); + + /** + * Schedules C++ for just-in-time compilation. This must be called only + * before `compile()`. + * + * @param d file to read C++ code from + */ + void add(const std::filesystem::path& p) { _files.push_back(p); } + + /** + * Compiles all added C++ source files into internal bitcode. + * + * HILTI-level errors will be logged through the global `Logger`. If the + * C++ code contains any errors, that will currently be reported directly + * to stderr. + * + * @return true if all files have been succesfully compiled + */ + bool compile(); + + /** + * Compiles the linked bitcode into native executable code and makes it + * available inside the current process. This must be called opnly after + * `link()` + * + * Errors will be logged through the global `Logger` + * + * @return succes if the bitcode has been succesfully JITed, otherwise an appropiate error + */ + Result jit(); + + /** + * Returns already JITed code as a shared library that can be cached. + * This must be called only after `jit()` has been called and succeeded. + */ + Result> retrieveLibrary() const; + + /** + * Initalizes the HILTI runtime system. This is necessary before any of + * the compiled code can be used, and must be done only after `jit()` has + * succeeded. Initializing the runtime will directly execute any + * initialization logic part of the compiled code, such as intialization + * HILTI globals and running module-global HILT statements. + * + * @return true if the runtime has bee succesfully initialized + */ + bool initRuntime(); + + /** + * Shuts down the runtime system. Calling this is optional, it will run + * at JIT destruction time at the latest. + */ + bool finishRuntime(); + + /** + * Returns true if any source files have been added that need to be + * compiled. If this returns false, its safe to skip calling `compile()` + * (though still doing so won't hurt). + */ + bool needsCompile() { return _codes.size() || _files.size(); } + + /** Returns the compiler context in use. */ + auto context() const { return _context; } + + /** Returns the compiler options in use. */ + auto options() const { return _context->options(); } + + /** + * Returs a string identifhing the underlying compiler used for JIT + * compilation. Currently, that's a always a version of `clang`. + */ + static std::string compilerVersion(); + +private: + std::shared_ptr _context; + std::vector _files; // all added source files + std::vector _codes; // all C++ code units to be compiled + std::vector _libraries; // all precomiled modules we know about + std::unique_ptr _jit; // JIT backend +}; + +} // namespace hilti diff --git a/hilti/include/compiler/plugin.h b/hilti/include/compiler/plugin.h new file mode 100644 index 000000000..37ce9fe44 --- /dev/null +++ b/hilti/include/compiler/plugin.h @@ -0,0 +1,296 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +class Unit; +class Context; + +namespace printer { +class Stream; +} // namespace printer + +/** + * Compiler plugin that can hook into the compilation process that's driven + * by `Unit`. + * + * A plugin gets access the the AST at all major stages. In particular it can + * add support implement support for new language using HILTI as its code + * generation backend by providing a parse method building its AST, along + * with a transformation method converting any non-standard nodes HILTI + * equivalents. + * + * A plugin implements a set of hook methods that get called by the + * compilation process at the appropiate times. + * + * @note HILTI compilation itself is also implemented through a default + * plugin that's always available. `Unit` cycles through all available + * plugins during the compilation processm, including that default plugin. + */ +struct Plugin { + /** Helper template to define the type of hook methods. */ + template + using Hook = std::optional>; + + /** Name of the plugin. */ + std::string component; + + /** Extension for source files that the plugin handles. Must include the leading `.`. */ + std::filesystem::path extension; + + /** + * Additional C++ include files that the plugin needs to have added to + * generated C++ code. + */ + std::vector cxx_includes; + + /** + * Hook called to retrieve paths to search when importing modules that + * this plugin handles. + * + * @param arg1 compiler context that's in use + * @return directories to search + */ + Hook, std::shared_ptr> library_paths; + + /** + * Hook called to parse input file that this plugin handles. + * + * @param arg1 compiler context that's in use + * #param arg2 input stream to parse + * @param arg3 file associated with the input stream + * @return directories to search + */ + Hook, std::istream&, std::filesystem::path> parse; + + /** + * Hook called to perform coercion of a `Ctor` into another of a given target type. + * + * If the plugin knows how to handle the coercion, the hook returns a new + * `Ctor` that's now of the target type. + * + * @param arg1 compiler context that's in use + * @param arg2 ctor that needs coercion + * @param arg3 target type for ctor + * @param arg4 coercion style to use + * @return new ctor if plugin performed coercion + */ + Hook, Ctor, const Type&, bitmask> coerce_ctor; + + /** + * Hook called to approved coercion of an expression into a different + * type. + * + * If the plugin knows it can handle the coercion, it returns the + * resulting coerced `Type`. If so, it must then also provide an + * `apply_coercions` hook that will later be called to perform the actual + * coercion during code generation. + * + * @param arg1 compiler context that's in use + * @param arg2 type that needs coercion + * @param arg3 target type for coercion + * @param arg4 coercion style to use + * @return new type if plugin can handnle this coercion + */ + Hook, Type, const Type&, bitmask> coerce_type; + + /** + * Hook called to build the scopes in a module's AST. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may modify the AST + * @param arg3 current unit being compiled + * @return true if the hook modified the AST in a substantial way + */ + Hook, const std::vector>&, Unit*> build_scopes; + + /** + * Hook called to resolved IDs in a module's AST. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may modify the AST + * @param arg3 current unit being compiled + * @return true if the hook modified the AST in a substantial way + */ + Hook, Node*, Unit*> resolve_ids; + + /** + * Hook called to resolved operators in a module's AST. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may modify the AST + * @param arg3 current unit being compiled + * @return true if the hook modified the AST in a substantial way + */ + Hook, Node*, Unit*> resolve_operators; + + /** + * Hook called perform coercions. This must carry out all the coercions + * that `coerce_type` has indicated as valid. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may modify the AST + * @param arg3 current unit being compiled + * @return true if the hook modified the AST in a substantial way + */ + Hook, Node*, Unit*> apply_coercions; + + /** + * Hook called to validate correctness of an AST, pre-transformation. + * Any errors must be reported through the global `Logger`. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may not modify the AST + * @param arg3 current unit being compiled + */ + Hook, const Node&, Unit*> pre_validate; + + /** + * Hook called to validate correctness of an AST, post-transformation. + * Any errors must be reported through the global `Logger`. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may not modify the AST + * @param arg3 current unit being compiled + */ + Hook, const Node&, Unit*> post_validate; + + /** + * Hook called to validate correctness of AST nodes that a modules preserved before transformation. + * The hooks runs just before the ``post_validate`` hook. + * Any errors must be reported through the global `Logger`. + * + * @param arg1 compiler context that's in use + * @param arg2 preserved nodes to validate + * @param arg3 current unit being compiled + */ + Hook, const std::vector&, Unit*> preserved_validate; + + /** + * Hook called to replace any custom AST nodes with standard HILTI + * nodes. Note that this may be called multiple times while ASTs are built. + * + * @param arg1 compiler context that's in use + * @param arg2 root node of AST; the hook may modify the AST + * @param arg3 boolean that's true if this hook runs for the first time on this AST. + * @param arg4 current unit being compiled + * @return true if the hook modified the AST in a substantial way + */ + Hook, Node*, bool, Unit*> transform; + + /** + * Hook called to print an AST back as source code. The hook gets to + * choose if it wants to print the node itself, or fall back to the default printer. + * + * @param arg1 root of AST to print + * @param arg2 stream to print to + * @return true if the hook printed the AST, false to fall back to default + */ + Hook print_ast; +}; + +class PluginRegistry; + +namespace plugin { +/** Returns the global plugin registry. It's a singleton instance. */ +PluginRegistry& registry(); +} // namespace plugin + +/** + * Maintains the set of all available plugins. `registry()` returns the + * global singleton registry instance. + */ +class PluginRegistry { +public: + PluginRegistry(); + + /** Returns a vector of all currentlh registered plugins. */ + const std::vector& plugins() const { return _plugins; } + + /** + * Returns the plugin handling a module with a given file extension, if + * available. + * + * @param ext extension, including the leading `.` + * @return plugin if any has been register for the extension + */ + Result pluginForExtension(std::filesystem::path ext) const; + + /** + * Checks if at least one plugin implements a given hook. + * + * \tparam PluginMember the hook + * \return true if there's an implemention for the hook + */ + template + bool hasHookFor(PluginMember hook) { + for ( const auto& p : plugin::registry().plugins() ) { + if ( p.*hook ) + return true; + } + + return false; + } + + /** + * Checks if there a plugin registered for a specific file extension. + * + * @param ext extension, including the leading `.` + * \return true if there's a plugin for this extension + */ + bool supportsExtension(std::filesystem::path ext) const { return pluginForExtension(std::move(ext)); } + + /** Returns a vector of all exensions that registered set of plugins handles. */ + auto supportedExtensions() const { + return util::transform(_plugins, [](auto& p) { return p.extension; }); + } + + /** + * Registers a plugin with the registry. + * + * @note This method should nornally not be called directly, use + * `plugin::Register()` instead. + * + * @param p plugin to register + */ + void register_(const Plugin& p) { _plugins.push_back(p); } + +private: + std::vector _plugins; +}; + +namespace plugin { +/** + * Helper class to register a plugin at startup. To add a plugin, create a + * global `Register` instance to register it. + */ +class Register { +public: + /** + * Registers a plugin with the global `registry()`. + * + * @param p plugin to register + */ + Register(const Plugin& p) { registry().register_(p); } +}; + +} // namespace plugin + +} // namespace hilti diff --git a/hilti/include/compiler/printer.h b/hilti/include/compiler/printer.h new file mode 100644 index 000000000..333439ce7 --- /dev/null +++ b/hilti/include/compiler/printer.h @@ -0,0 +1,98 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +namespace hilti::printer { + +class Stream { +public: + Stream(std::ostream& s, bool _compact) : _stream(s), _compact(_compact), _nl(_compact ? ' ' : '\n') {} + + void beginLine() { _stream << std::string(_indent * 4, ' '); } + void endLine() { _stream << (_compact ? ' ' : '\n'); } + void emptyLine() { + if ( _wrote_nl ) + return; + + _stream << (_compact ? ' ' : '\n'); + _wrote_nl = true; + } + + char newline() const { return _nl; } + + const ID& currentScope() const { return _scopes.back(); } + void pushScope(ID id) { _scopes.push_back(std::move(id)); } + void popScope() { _scopes.pop_back(); } + + bool isCompact() { return _compact; } + + bool isExpandSubsequentType() const { return _expand_subsequent_type; } + void setExpandSubsequentType(bool expand) { _expand_subsequent_type = expand; } + + bool isFirstInBlock() const { return _first_in_block; } + bool isLastInBlock() const { return _last_in_block; } + void setPositionInBlock(bool first, bool last) { + _first_in_block = first; + _last_in_block = last; + } + + auto indent() const { return _indent; } + void incrementIndent() { ++_indent; } + void decrementIndent() { + --_indent; + _first_in_block = _last_in_block = false; + } + + template + Stream& operator<<(const T& t) { + if constexpr ( std::is_base_of::value ) { + if ( auto id = Type(t).typeID() ) + _stream << *id; + } + else + hilti::detail::printAST(t, *this); + + return *this; + } + + template + Stream& operator<<(const T& t) { + _wrote_nl = false; + _stream << t; + _expand_subsequent_type = false; + return *this; + } + + // Output lists. + template + Stream& operator<<(std::pair p) { + bool first = true; + for ( auto& i : p.first ) { + if ( ! first ) + _stream << p.second; + + (*this) << i; + first = false; + } + + return *this; + } + +private: + std::ostream& _stream; + bool _compact; + char _nl; + int _indent = 0; + bool _wrote_nl = false; + bool _first_in_block = false; + bool _last_in_block = false; + bool _expand_subsequent_type = false; + std::vector _scopes = {""}; +}; + +} // namespace hilti::printer diff --git a/hilti/include/compiler/unit.h b/hilti/include/compiler/unit.h new file mode 100644 index 000000000..e2a5f50cd --- /dev/null +++ b/hilti/include/compiler/unit.h @@ -0,0 +1,320 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace linker { +/** + * Linker meta data associated with a HILTI unit. When HILTI compiles a + * module, it records information the HILTI's internal linker, including for + * example any global variables the moduel defines as well what + * initialization code it needs. The HILTI linker then later combines the + * meta data from all HILTI modules and generated additional C++ code from it + * for use by the HILTI runtime library. + */ +using MetaData = detail::cxx::linker::MetaData; +} // namespace linker + +/** + * Container for a single HILTI code module. For each HULTI source file, one + * compiler unit gets instantiated. That unit then drives the process to + * compile module comp into C++ code. While that's in progress, the unit + * maintains state about the process, such as a cache of all external modules + * imported by the one that's being compiled. + */ +class Unit { +public: + /** Returns the root node of the module AST's. */ + NodeRef module() { + assert(_id); + return NodeRef(imported(_id)); + } + + /** + * Returns the ID of the unit's top-level module (i.e., the one being + * compiled). + */ + auto id() const { return _id; } + + /** + * Returns the path associated with the unit's top-level module (i.e., + * the one being compiled). + */ + auto path() const { return _path; } + + /** + * Compiles the unit's module AST into its final internal representation. + * @return set if succesful, or an appropiate error result + */ + Result compile(); + + /** Triggers generation of C++ code from the compiled AST. */ + Result codegen(); + + /** + * + * Prints out a cimpled HILTI module by recreasting its code from the + * internal AST. Must be called only after `compile()` was succesful. + * + * @param out stream to print the code to + * @return set if succesful, or an appropiate error result + */ + Result print(std::ostream& out) const; + + /** + * Prints out C++ prototypes that host applications can use to interface + * with the generated C++ code. Must be called only after `compile()` was + * succesful. + * + * @param out stream to print the code to + * @return set if succesful, or an appropiate error result + */ + Result createPrototypes(std::ostream& out); + + /** + * Returns the generated C++ code. Must be called only after `compile()` + * was succesful. + * + * @return code wrapped into the JIT's container class + */ + Result cxxCode() const; + + /** + * Makes an external HILTI module available to the one this unit is + * compiling. Essentially, this implements the HITLI's `import` + * statement. Importing another module means that the compiled module + * will know how to access the other one's functinality. However, that + * external module will still need to be compiled itself as well; and + * then all the compiled modules need to be linked together. + * + * This version of the `import` method imports by module ID: it searches + * `Configuration::hilti_library_paths` for a file with a corresponding + * name and extension. + * + * @param id ID of module to import + * @param ext file name extension to search; `.hlt` is HILTI's standard extension, but plugins can add support for + * other extensions as well + * @param scope if given, qualifies the ID with a path prefix to find the + * module (e.g., a scope of `a.b.c` will search the module in `/a/b/c/`.) + * @param dirs additional directories to search first + * @return the modules' cache index if successfull,or an appropiate error result if not + */ + Result import(const ID& id, const std::filesystem::path& ext, std::optional scope = {}, + std::vector search_dirs = {}); + + /** + * Makes an external HILTI module available to the one this unit is + * compiling. See `import(const ID, const std::filesystem::path)` for + * more details on importng. + * + * This version of the `import` method imports directly from a given + * file. + * + * @param path to the file to import + * @return the modules' cache index if successfull,or an appropiate error result if not + */ + Result import(const std::filesystem::path& path); + + /** + * Returns the AST for an imported module. + * + * @param id module ID + * @return Reference to the root node of the imported module's AST + * @exception `std::out_of_range` if no module of that name has been imported yet + */ + Node& imported(const ID& id) const; + + /** + * Returns set of all imported modules so far. + * + * @param code_only if true include only dependencies that require independent compilation themselves + */ + std::set allImported(bool code_only = false) const; + + /** + * Returns true if an imported module provides code that needs + * independent compilation to resolve references at link-time. + * + * @return a boolean that true if the module provides code for + * compilation, or an error value if no such module is known. + */ + Result requiresCompilation(const ID& id) { + if ( auto x = _lookupModule(id) ) + return x->requires_compilation; + + return result::Error("unknown module"); + } + + /** + * Returns the unit's meta data for the internal HILTI linkger. + * + * @return meta data, or an error if no code has been compiled yet + */ + Result linkerMetaData() const { + if ( _cxx_unit ) + return _cxx_unit->linkerMetaData(); + + return result::Error("no C++ code compiled"); + } + + /** + * Returns true if this unit has been compiled from HILTI source. This is + * usually the case, but we also represent HILTI's linker output as a + * unit and there's no corresponding HILTI source code for that. + */ + bool isCompiledHILTI() const { return _have_hilti_ast; } + + /** Returns the compiler context in use. */ + std::shared_ptr context() const { return _context; } + + /** Returns the compiler options in use. */ + const Options& options() const { return _context->options(); } + + /** + * Factory method that instantiastes a unit from an existing HILTI module + * that's be compiled. + * + * This method also caches the module with the global context. Note that + * the module's ID or path must not exist with the context yet. + * + * @param context glocal compiler context + * @param module HILTI module to compile, of which the unit will take ownership + * @param path path associated with the module, if any + * @return instantiated unit, or an appropiate error result if operation failed + */ + static Result fromModule(const std::shared_ptr& context, hilti::Module&& module, + const std::filesystem::path& path = ""); + + /** + * Factory method that instantiastes a unit from an existing source file that it will parse. + * + * This method also caches the module with the global context. Note that + * the module's ID or path must not exist with the context yet. + * + * @param context glocal compiler context + * @param path path to parse the module from + * @return instantiated unit, or an appropiate error result if operation failed + */ + static Result fromSource(const std::shared_ptr& context, const std::filesystem::path& path); + + /** + * Factory method that instantiates a unit from an existing HILTI module + * alreached cached by the global context. + * + * @param context glocal compiler context + * @param id ID of the cached module + * @return instantiated unit, or an appropiate error result if operation failed + */ + static Result fromCache(const std::shared_ptr& context, const hilti::ID& id); + + /** + * Factory method that instantiates a unit from an existing HILTI module + * alreached cached by the global context. + * + * @param context glocal compiler context + * @param path path of the cached module + * @return instantiated unit, or an appropiate error result if operation failed + */ + static Result fromCache(const std::shared_ptr& context, const std::filesystem::path& path); + + /** + * Factory method that instantiates a unit from existing C++ source code that's to compiled. + * + * @param context glocal compiler context + * @param path path associated with the C++ code, if any + * @return instantiated unit, or an appropiate error result if operation failed + */ + static Result fromCXX(std::shared_ptr context, detail::cxx::Unit cxx, + const std::filesystem::path& path = ""); + + /** + * Entry point for the HILTI linker, The linker combines meta data from + * several compiled HILTI modules and creates an additional unit from it, + * with its C++ code representing logic the HILTI runtime library will + * draw upon. + * + * @param context compiler context to use + * @param mds set of meta data from modules to be linked together + * @return a unit representing additional C++ code that the modules need to function + */ + static Result link(const std::shared_ptr& context, const std::vector& mds); + + /** + * Reads linker meta from a file. This expects the file to contain linker + * meta somewhere inside a appropiately marked block bracked by by + * C-style comments. When printing the generated C++ code for a compiled + * HILTI module, it will include that block. In other words, you can just + * save the C++ and reread the meta data with this method. + * + * @param input stream to read from + * @param path file associated with the stream, for logging and error reporting + * + * @return If the input had valid meta data, the 1st element is true and + * the second contains it. If the inout was valid but had no meta data + * included, the 1st element is true whiule the 2nd remains unset. If + * there was an error reading the input, the 1st element is false and the + * 2nd undefined. + */ + static std::pair> readLinkerMetaData( + std::istream& input, const std::filesystem::path& path = ""); + +private: + // Private constructor initializing the unit's meta data. Note that the + // corresponding module must then be imported into the unit as well. + // Nornmally you'd use the static ``Unit::from*()`` functions to do that + // while creating a unit. + Unit(std::shared_ptr context, ID id, std::filesystem::path path, bool have_hilti_ast) + : _context(std::move(context)), _id(std::move(id)), _path(std::move(path)), _have_hilti_ast(have_hilti_ast) {} + + // Returns a list of all currently known/imported modules. + std::vector> _currentModules() const; + + // Looks up a module by its ID. The module must have been imported into + // the unit to succeed. Assuming so, it returns the context's cache entry + // for the module. + std::optional _lookupModule(const ID& id) const; + + // Backend for the public import() methods. + Result _import(const std::filesystem::path& path, std::optional expected_name); + // Runs the validation pass (unless disabled). + Result _validateAST(const Node& module); + // Updates the requires_compilation flags for all of a module's imports. + void _determineCompilationRequirements(const Node& module); + // Sends a debug dump of a module's AST to the global logger. + void _dumpAST(const Node& module, const logging::DebugStream& stream, const std::string& prefix, int round = 0); + // Sends a debug dump of all modules parsed so far to the global logger. + void _dumpASTs(const logging::DebugStream& stream, const std::string& prefix, int round = 0); + // Sends a debug dump of a module's AST to an output stream. + void _dumpAST(const Node& module, std::ostream& stream, const std::string& prefix, int round = 0); + // Sends a debug dump of all modules parsed so far to an output stream. + void _dumpASTs(std::ostream& stream, const std::string& prefix, int round = 0); + // Records a debug dump of all modules parsed so far to disk. + void _saveIterationASTs(const std::string& prefix, int round = 0); + + // Parses a source file with the appropiate plugin. + static Result parse(const std::shared_ptr& context, const std::filesystem::path& path); + + std::shared_ptr _context; // global context + ID _id; // ID of top-level module being compiled by this unit + std::filesystem::path _path; // path of top-level module being compiled by this unit + bool _have_hilti_ast; // true if the top-level module comes with a HILTI AST (normally the case, but not for the + // linker's C++ code) + std::set _modules; // set of all module ASTs this unit has parsed and processed (inc. imported ones) + std::optional _cxx_unit; // compiled C++ code for this unit, once available +}; + +} // namespace hilti diff --git a/hilti/include/config.h.in b/hilti/include/config.h.in new file mode 100644 index 000000000..03448d93f --- /dev/null +++ b/hilti/include/config.h.in @@ -0,0 +1,109 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +#cmakedefine CXX_FILESYSTEM_IS_EXPERIMENTAL +#define CXX_FILESYSTEM_HEADER "@CXX_FILESYSTEM_HEADER@" + +#cmakedefine HILTI_HAVE_JIT +#cmakedefine HILTI_HAVE_SANITIZER + +#ifdef CXX_FILESYSTEM_IS_EXPERIMENTAL +#include +namespace std { namespace filesystem = experimental::filesystem; } // NOLINT +#else +#include +#endif + +namespace hilti { + +/** + * Stores compile-time configuration information. + */ +struct Configuration { + /** + * Default constructor that initializes all settings relative to the + * currently executing binary. To change that, call `initLocation()`. + */ + Configuration(); + + /** + * Reconfigures the configuration object to adapt all paths to + * potentially running out of the build directory. + * + * @param use_build_directory true to adapt paths to build directory, false for installation directory + */ + void initLocation(bool use_build_directory); + + /** + * Reconfigures the configuration object to adapt all paths to + * potentially running out of the build directory. This version bases the + * decision on the path of the current executable: if it's inside the + * source directory, we're assuming we're running out of build directory. + * + * @param argv0 path to current execuable + */ + void initLocation(const std::string_view& argv0); + + /** + * Reconfigures the configuration object to adapt all paths to + * potentially running out of the build directory. This version bases the + * decision on the path of the current executable: if it's inside the + * source directory, we're assuming we're running out of build directory. + * + * \note We need this overload as otherwise the boolean version + * would be picked for C strings. + * + * @param argv0 path to current execuable + */ + void initLocation(const char* argv0); + + bool uses_build_directory; /**< True if all information pertains to running outside of the build directory. */ + + std::filesystem::path cxx; /**< Full path to C++ compiler */ + std::filesystem::path distbase; /**< base directory of HILTI source distribution */ + std::filesystem::path hiltic; /**< Full path to `hiltic` binary */ + std::filesystem::path install_prefix; /**< HILTI install prefix */ + std::filesystem::path lib_directory; /**< Full path to directory where HILTI libraries are stored */ + std::filesystem::path build_directory; /**< Full path to directory where HILTI was built */ + std::vector + hilti_library_paths; /**< Default search path for HILTI modules, separated by `:` */ + + int version_number; /**< Single version number encoding major/minor/patch levels */ + int version_major; /**< Major number of the HILTI version */ + int version_minor; /**< Minor number of the HILTI version */ + int version_patch; /**< Patch number of the HILTI version */ + std::string version_prerelease; /**< Prelease number of the HILTI version */ + std::string version_string; /* Readable version string, without git information */ + std::string version_string_long; /* Readable version string, including git information */ + + bool jit_enabled; /** True if JIT support has been compiled in. */ + std::filesystem::path jit_clang_executable; /**< Path to clang++ JITing */ + std::filesystem::path jit_clang_resource_dir; /**< Clang's resource directory for JITing */ + std::filesystem::path jit_c_system_include_dirs; /**< Additional include directories for C library headers */ + std::filesystem::path jit_cxx_system_include_dirs; /**< Additional include directories for C++ library headers */ + + std::vector runtime_cxx_flags_debug; /**< C++ compiler flags when compiling custom code in debug mode + that uses the HILTI runtime library */ + std::vector runtime_ld_flags_debug; /**< Linker flags when compiling custom code in debug mode that + uses the HILTI runtime library */ + std::vector runtime_cxx_flags_release; /**< C++ compiler flags when compiling custom code in release + mode that uses the HILTI runtime library */ + std::vector runtime_ld_flags_release; /**< Linker flags when compiling custom code in release mode that + uses the HILTI runtime library */ +private: + void init(bool use_build_location); +}; + +/** + * Returns a reference to the global configuration information. This is the + * same information that `hilti-config` reports as well. + */ +extern Configuration& configuration(); + +} // namespace hilti diff --git a/hilti/include/global.h b/hilti/include/global.h new file mode 100644 index 000000000..57bc9bb09 --- /dev/null +++ b/hilti/include/global.h @@ -0,0 +1,130 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { + +/** + * Parses a HILTI source file into an AST. + * + * @param in stream to read from + * @param filename path associated with the input + * + * Returns: The parsed AST, or a corresponding error if parsing failed. + */ +Result parseSource(std::istream& in, const std::string& filename); + +/** + * Prints out a debug representation of an AST node to a debug stream. The + * output will include all the node's children recursively. + * + * @param out stream to print to + * @param node the node + * @param include_scopes if true, include a dump of each node's identifier + * scope + */ +extern void render(std::ostream& out, const Node& node, bool include_scopes = false); + +/** + * Log a debug representation of an AST node to a debug stream. The output + * will include all the node's children recursively. + * + * @param stream + * @param node the node + * @param include_scopes if true, include a dump of each node's identifier + * scope + */ +extern void render(logging::DebugStream stream, const Node& node, bool include_scopes = false); + +/** + * Print out an AST node as HILTI source. + * + * @note Usually, this function should be used on an AST's root node (i.e., + * the module). The function accepts other nodes, but may not always produce + * currect code for them. + * + * @param out stream to print to + * @param node the node + * @param compact if true, print a compact one-line representation (e.g., for + * use in error messages) + */ +inline void print(std::ostream& out, const Node& node, bool compact = false) { node.print(out, compact); } + +/** + * Logs all errors currently recorded in an AST. + * + * @param root node of AST + * @return true if aany errors are recorded in the AST + */ +bool reportErrorsInAST(const Node& root); + +namespace detail { +/** Internal backend to `hilti::lookupID()`. */ +extern std::pair>> lookupID(const ID& id, const Node& n); +} // namespace detail + +/** + * Looks up a still unresolved ID inside an AST. The ID is expected to + * resolve to exactly one declaration of an expected type, and must be + * exported if inside another module; otherwise an error is flagged. + * + * @tparam D class implementing the `Declaration` interface that we expecting the ID to resolve to + * @param id id to look up + * @param p AST position where to start the lookup; we'll traverse up the AST from there + * @param n node to use for error reporting if something goes wrong + * @return node if resolved, or an appropiate error if not + */ +template +Result> lookupID(const ID& id, const visitor::Position& p) { + auto i = p.path.rbegin(); + while ( i != p.path.rend() ) { + auto [stop, resolved] = detail::lookupID(id, **i); + + if ( ! stop ) { + if ( auto t = (*i)->tryAs(); t && t->hasFlag(type::Flag::NoInheritScope) ) { + // Advance to module scope directly. + while ( ++i != p.path.rend() ) { + if ( (*i)->isA() ) + break; + } + } + else + ++i; + + continue; + } + + if ( ! resolved ) + return std::move(resolved); + + if ( auto d = (*resolved).first->tryAs() ) { + if ( ! resolved->second.namespace_() ) { + // If it's from module's scope, qualify the ID. + if ( auto m = (*i)->tryAs() ) + return std::make_pair(resolved->first, ID(m->id(), resolved->second)); + } + + return std::move(resolved); + } + + return result::Error(util::fmt("ID '%s' does not resolve to a %s (but to %s)", id, typeid(D).name(), + (*resolved).first->as().displayName())); + } + + return result::Error(util::fmt("unknown ID '%s'", id)); +} + + +} // namespace hilti diff --git a/hilti/include/hilti b/hilti/include/hilti new file mode 120000 index 000000000..f5030fe88 --- /dev/null +++ b/hilti/include/hilti @@ -0,0 +1 @@ +../include \ No newline at end of file diff --git a/hilti/include/hilti.h b/hilti/include/hilti.h new file mode 100644 index 000000000..0480ce16d --- /dev/null +++ b/hilti/include/hilti.h @@ -0,0 +1,14 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/json.h b/hilti/include/json.h new file mode 100644 index 000000000..667627a2f --- /dev/null +++ b/hilti/include/json.h @@ -0,0 +1,14 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-overlap-compare" +#endif + +#include + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/hilti/include/rt/NOTES b/hilti/include/rt/NOTES new file mode 100644 index 000000000..3d05e2a4d --- /dev/null +++ b/hilti/include/rt/NOTES @@ -0,0 +1,19 @@ +- Generally, keep global state to a mininum and add it to + hilti::rt::detail::GlobalState instead of creating actual globals. + This applies to static class members, too. + +- If you really have to create a global or static class member (which + should be rare), do not use types that have global constructors. JIT + is easier if we don't need to support them (and we don't). + +- Make sure accesses to state in hilti::rt::detail::GlobalState is + thread-safe. + + TODO: Actually right now it is not, need to go througn and add + synchronization where necessary. (Or maybe an accessor object that + locks the state automatically) + +- TODO: The separation of internal details and public API isn't great + yet. Should move factor out much of the headers into a separate + `detail/` tree, and then also move more from the public namespace + into the `detail` namespace. diff --git a/hilti/include/rt/backtrace.h b/hilti/include/rt/backtrace.h new file mode 100644 index 000000000..0852b10d4 --- /dev/null +++ b/hilti/include/rt/backtrace.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti::rt { + +/** Captures a stack backtrace at construction time. */ +class Backtrace { +public: + Backtrace(); + + const auto& backtrace() const { return _backtrace; } + +private: + std::vector _backtrace; +}; + +/** Wrapper around the ABI's C++ demangle function. */ +inline std::string demangle(const std::string& symbol) { + int status; + char* dname = abi::__cxa_demangle(symbol.c_str(), nullptr, nullptr, &status); + std::string x = (dname && status == 0) ? dname : symbol; + if ( dname ) + free(dname); // NOLINT + + return x; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/compiler-setup.h b/hilti/include/rt/compiler-setup.h new file mode 100644 index 000000000..1c8143534 --- /dev/null +++ b/hilti/include/rt/compiler-setup.h @@ -0,0 +1,15 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// This file is included at the beginning of each generated +// C++ file to setup compiler options. + +#pragma once + +#if __clang__ +// Clang-specific options. +#pragma clang diagnostic ignored "-Wunused-comparison" +#endif + +#if __GNUC__ +// GCC-specific options. +#endif diff --git a/hilti/include/rt/config.h.in b/hilti/include/rt/config.h.in new file mode 100644 index 000000000..c8c23f8f1 --- /dev/null +++ b/hilti/include/rt/config.h.in @@ -0,0 +1,15 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#cmakedefine CXX_FILESYSTEM_IS_EXPERIMENTAL +#define CXX_FILESYSTEM_HEADER "@CXX_FILESYSTEM_HEADER@" + +#cmakedefine HILTI_HAVE_BACKTRACE +#ifdef HILTI_HAVE_BACKTRACE +#include <${Backtrace_HEADER}> +#endif + +#cmakedefine HILTI_HAVE_SANITIZER diff --git a/hilti/include/rt/configuration.h b/hilti/include/rt/configuration.h new file mode 100644 index 000000000..473175d4b --- /dev/null +++ b/hilti/include/rt/configuration.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti::rt { + +/** Configuration parameters for the HILTI runtime system. */ +struct Configuration { + Configuration(); + + /** Stack size for fibers. */ + size_t fiber_stack_size = 100 * 1024 * 1024; // This is generous. + + /** Maximum size of pool of recycalable fibers. */ + size_t fiber_max_pool_size = 1000; + + /** File where debug output is to be sent. Default is stderr. */ + std::optional debug_out; + + /** Show backtraces when reporting unhandled exceptions. */ + bool show_backtraces = false; + + /** abort() instead of throwing HILTI exceptions. */ + bool abort_on_exceptions = false; + + /** Colon-separated list of debug streams to enable. Default comes from HILTI_DEBUG. */ + std::string debug_streams; +}; + +namespace configuration { +/** + * Returns a copy of the current global configuration. To change the + * configuration, modify it and then pass it back to `set()`. + */ +extern Configuration get(); + +/** + * Sets new configuration values. Usually one first retrieves the current + * configuration with `get()` to then apply any desired changes to it. + * + * @param cfg complete set of new confifuration values + */ +extern void set(Configuration cfg); + +} // namespace configuration +} // namespace hilti::rt diff --git a/hilti/include/rt/context.h b/hilti/include/rt/context.h new file mode 100644 index 000000000..34f372bc8 --- /dev/null +++ b/hilti/include/rt/context.h @@ -0,0 +1,144 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace hilti::rt { + +/** + * Thread execution context. One of these exists per virtual thread, plus one + * for the main thread. + * + * The type's fields are considered implementation details and shouldn't be + * accessed or modified by external code. + */ +struct Context { + /** + * @param vid virtual thread ID of the thread that will use the context + */ + explicit Context(vthread::ID vid); + ~Context(); + + Context() = delete; + Context(const Context&) = delete; + Context(Context&&) = delete; + Context& operator=(const Context&) = delete; + Context& operator=(Context&&) = delete; + + /** + * The ID of the virtual thread this context belongs to. `vthread::Main` + * for the main thread. + */ + vthread::ID vid; + + /** + * Current resumable if we're inside a fiber so that yielding is + * supported. Ownership remains with original caller. + */ + resumable::Handle* resumable = nullptr; + + /** + * Pointer to an array of (per thread) global variables allocated by the + * linker code. Each array entry corresponds to the globals of one HILTI + * module. + */ + std::vector> hilti_globals; + + /** A user-defined cookie value that's carried aroudn with the context. */ + void* cookie = nullptr; + + /** Current indent level for debug messages. */ + uint64_t debug_indent{}; +}; + +namespace context { +namespace detail { + +/** + * Helper returning a reference to a thread-local variable storing the + * current context. We can't access the pointer directly as that leads to + * trouble with JITted code not resolving it correctly. + * + * Normally, this function should not be used; use `get()`/`set()` instead. + */ +extern Context*& current(); + +/** Returns the context for the main thread. */ +extern Context* master(); + +/** Returns the context set for the current hardware thread. */ +inline auto get() { + assert(current()); + return current(); +} + +/** + * Sets the current context. This will be visible to code inside the current + * hardware thhread. + */ +inline auto set(Context* ctx) { + auto old = current(); + current() = ctx; + return old; +} + +/** + * Utility class that sets the current context's `resumable` field during its life-time. + */ +class ResumableSetter { +public: + explicit ResumableSetter(resumable::Handle* r) { + old = get()->resumable; + get()->resumable = r; + } + + ~ResumableSetter() { get()->resumable = old; } + + ResumableSetter() = delete; + ResumableSetter(const ResumableSetter&) = delete; + ResumableSetter(ResumableSetter&&) = delete; + ResumableSetter& operator=(const ResumableSetter&) = delete; + ResumableSetter& operator=(ResumableSetter&&) = delete; + + resumable::Handle* old; +}; + +} // namespace detail + +/** Stores a user-defined cookie in the current context. */ +inline void saveCookie(void* cookie) { detail::get()->cookie = cookie; } + +/** Returns the user-defined cookie currently set in the current context. */ +inline void* cookie() { return detail::get()->cookie; } + +/** Cleares the user-defined cookie in the current context. */ +inline void clearCookie() { detail::get()->cookie = nullptr; } + +/** + * Executes a function inside the current context's fiber. + * + * @param f function to execute + * @param params arguments to pass into function + * @return resumable object to either retrieve result or resume if execution got postponed + */ +template +Resumable execute(Function f, Params&&... params) { + auto cb = [&](resumable::Handle* r) { + auto _ = detail::ResumableSetter(r); + return f(std::forward(params)...); + }; + + Resumable r(std::move(cb)); + r.run(); + return r; +} + +} // namespace context +} // namespace hilti::rt diff --git a/hilti/include/rt/debug-logger.h b/hilti/include/rt/debug-logger.h new file mode 100644 index 000000000..3a7c00880 --- /dev/null +++ b/hilti/include/rt/debug-logger.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace hilti::rt::detail { + +/** Logger for runtime debug messages. */ +class DebugLogger { +public: + DebugLogger(std::filesystem::path output); + + void print(const std::string& stream, const std::string& msg); + void enable(const std::string& streams); + + bool isEnabled(const std::string& stream) { return _streams.find(stream) != _streams.end(); } + + void indent(const std::string& stream) { + if ( isEnabled(stream) ) + _streams[stream] += 1; + } + + void dedent(const std::string& stream) { + if ( isEnabled(stream) ) + _streams[stream] -= 1; + } + +private: + std::filesystem::path _path; + std::optional _output; + std::map _streams; +}; + +} // namespace hilti::rt::detail diff --git a/hilti/include/rt/deferred-expression.h b/hilti/include/rt/deferred-expression.h new file mode 100644 index 000000000..468714b63 --- /dev/null +++ b/hilti/include/rt/deferred-expression.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti::rt { + +/** + * Wrapper for an expression that's evaluation is deferred until requested. + * The expression must be wrapped into a function call, and it's evaluation + * is requested through the wrapper's call operator. + */ +template +class DeferredExpression { +public: + DeferredExpression(std::function expr) : _expr(std::move(expr)) {} + DeferredExpression() = delete; + DeferredExpression(const DeferredExpression&) = default; + DeferredExpression(DeferredExpression&&) noexcept = default; + + ~DeferredExpression() = default; + + DeferredExpression& operator=(const DeferredExpression&) = default; + DeferredExpression& operator=(DeferredExpression&&) noexcept = default; + + Result operator()() const { return _expr(); } + +private: + std::function _expr; +}; + +namespace detail::adl { +template +inline std::string to_string(const DeferredExpression& x, adl::tag /*unused*/) { + return hilti::rt::to_string(x()); +} + +template +inline std::string to_string_for_print(const DeferredExpression& x, adl::tag /*unused*/) { + return hilti::rt::to_string_for_print(x()); +} +} // namespace detail::adl + +template +inline std::ostream& operator<<(std::ostream& out, const DeferredExpression& x) { + return out << to_string_for_print(x); +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/exception.h b/hilti/include/rt/exception.h new file mode 100644 index 000000000..e742c493b --- /dev/null +++ b/hilti/include/rt/exception.h @@ -0,0 +1,131 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace hilti::rt { + +/** + * HILTI's base exception type. All HILTI-side runtime exceptions are derived + * from this. + */ +class Exception : public std::runtime_error { +public: + /** + * @param desc message describing the situation + */ + Exception(const std::string& desc); + + /** + * @param desc message describing the situation + * @param location string indicating the location of the operation that failed + */ + Exception(std::string_view desc, std::string_view location); + + Exception() = delete; + Exception(const Exception&) = default; + Exception(Exception&&) noexcept = default; + Exception& operator=(const Exception&) = default; + Exception& operator=(Exception&&) noexcept = default; + + // Empty, but necessary to make exception handling work between library + // and host application. Presumably this: + // http://www.toptip.ca/2012/06/c-exceptions-thrown-from-shared-library.html + ~Exception() override; + + /** Returns the message associated with the exception. */ + auto description() const { return _description; } + + /** Returns the location associated with the exception. */ + auto location() const { return _location; } + + /** + * Returns a stack backtrace captured at the time the exception was + * thrown. + */ + auto backtrace() const { return _backtrace.backtrace(); } + +private: + Exception(const std::string& what, std::string_view desc, std::string_view location); + + std::string _description; + std::string _location; + Backtrace _backtrace; +}; + +#define HILTI_EXCEPTION(name, base) \ + class name : public base { \ + public: \ + using base::base; \ + }; + +#define HILTI_EXCEPTION_NS(name, ns, base) \ + class name : public ns::base { \ + public: \ + using ns::base::base; \ + }; + +/** Base class for exceptions thrown by the runtime system. */ +HILTI_EXCEPTION(RuntimeError, Exception) + +/** Base class for exceptions created by HILTI programs. */ +HILTI_EXCEPTION(UserException, Exception) + +/** Thrown when an `assert` statement fails. */ +HILTI_EXCEPTION(AssertionFailure, RuntimeError) + +/** Thrown when an invalid container index is accessed. */ +HILTI_EXCEPTION(IndexError, RuntimeError) + +/** * Thrown when a default-less `switch` statement hits case that's no covered. */ +HILTI_EXCEPTION(UnhandledSwitchCase, RuntimeError) + +/** + * Exception signaling that an operation could not complete due to lack of + * input or I/O delays. The operation should be retried when that situation + * may have changed. + * + * This is outside the standard exception hierarchy as it does not reflect an + * error condition. + */ +class WouldBlock : public std::runtime_error { +public: + using std::runtime_error::runtime_error; + + /** + * @param desc message describing the situation + * @param location string indicating the location of the operation that couldn't complete + */ + WouldBlock(const std::string& desc, const std::string& location) : WouldBlock(fmt("%s (%s)", desc, location)) {} +}; + +namespace exception { + +// Disables `Configuration::abort_on_exception` during its lifetime. +class DisableAbortOnExceptions { +public: + DisableAbortOnExceptions(); + ~DisableAbortOnExceptions(); + + DisableAbortOnExceptions(const DisableAbortOnExceptions&) = delete; + DisableAbortOnExceptions(DisableAbortOnExceptions&&) noexcept = delete; + DisableAbortOnExceptions& operator=(const DisableAbortOnExceptions&) = delete; + DisableAbortOnExceptions& operator=(DisableAbortOnExceptions&&) noexcept = delete; +}; + +/** Utility function printing out an uncaught exception to stderr. */ +void printUncaught(const Exception& e); + +/** Utility function printing out an uncaught exception to an output stream. */ +void printUncaught(const Exception& e, std::ostream& out); +} // namespace exception + +} // namespace hilti::rt diff --git a/hilti/include/rt/extension-points.h b/hilti/include/rt/extension-points.h new file mode 100644 index 000000000..813b4cb9f --- /dev/null +++ b/hilti/include/rt/extension-points.h @@ -0,0 +1,84 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti::rt { + +// See https://stackoverflow.com/questions/28070519/customization-points-and-adl. +namespace detail::adl { +struct tag {}; + +#if __GNUC__ +// TODO(robin): gcc9 doesn't allow to delete these, not sure why. Using +// externs instead, without implementation. +extern std::string to_string(); +extern void safe_begin(); +extern void safe_end(); +#else +std::string to_string() = delete; +void safe_begin() = delete; +void safe_end() = delete; +#endif + +} // namespace detail::adl + +/** Converts a HILTI runtime type into a string representation. */ +template +std::string to_string(const T& x) { + using detail::adl::to_string; + return to_string(x, detail::adl::tag{}); +} + +/** + * Returns a "safe" container start iterator. "safe" refers to the HILTI + * model: Accessing a safe iterator when the underlying container went away + * will be caught through an exception (rather than a crash). + */ +template +auto safe_begin(const T& x) { + using detail::adl::safe_begin; + return safe_begin(x, detail::adl::tag{}); +} +template +auto safe_begin(T& x) { + using detail::adl::safe_begin; + return safe_begin(x, detail::adl::tag{}); +} + +/** + * Returns a "safe" container end iterator. "safe" refers to the HILTI model: + * Accessing a safe iterator when the underlying container went away will be + * caught through an exception (rather than a crash). + */ +template +auto safe_end(const T& x) { + using detail::adl::safe_end; + return safe_end(x, detail::adl::tag{}); +} +template +auto safe_end(T& x) { + using detail::adl::safe_end; + return safe_end(x, detail::adl::tag{}); +} + +namespace detail { +template +inline std::string to_string_for_print(const T& x) { + return hilti::rt::to_string(x); +} +} // namespace detail + +/** + * Converts a HILTI runtime type into the string representation that + * `hilti::print()` outputs. This representastion is slightly different from + * the standard one (e.g., it doesn't enclose top-level strings in quotation + * marks). + */ +template +inline std::string to_string_for_print(const T& x) { + return detail::to_string_for_print(x); +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/fiber.h b/hilti/include/rt/fiber.h new file mode 100644 index 000000000..16bc7041a --- /dev/null +++ b/hilti/include/rt/fiber.h @@ -0,0 +1,229 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// TODO(robin): This may not be the most efficient solution for implementing fibers +// yet (due to using std::function, std::any) +// +// There are also at least two proposals for upcoming version of C++ that could implement this: +// - Coroutunes: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4680.pdf +// - Fibers: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0876r0.pdf + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include + +#undef print + +void _Trampoline(unsigned int y, unsigned int x); +} + +namespace hilti::rt { + +namespace detail { +class Fiber; +} // namespace detail + +namespace resumable { +/** Abstract handle providing access to a currently active function running inside a fiber. */ +using Handle = detail::Fiber; +} // namespace resumable + +namespace detail { + +/** + * A fiber implements a co-routine that can at any time yield control back to + * the caller, to be resumed later. This is the internal class implementing + * the main functionalty. It's used by `Resumable`, which provides the + * external interface. + */ +class Fiber { +public: + Fiber(); + ~Fiber(); + + Fiber(const Fiber&) = delete; + Fiber(Fiber&&) = delete; + Fiber& operator=(const Fiber&) = delete; + Fiber& operator=(Fiber&&) = delete; + + void init(std::function f) { + _state = State::Init; + _result = {}; + _exception = nullptr; + _function = std::move(f); + } + + void run(); + void yield(); + void resume(); + void abort(); + + auto&& result() { return std::move(_result); } + std::exception_ptr exception() const { return _exception; } + + static std::unique_ptr create(); + static void destroy(std::unique_ptr f); + static void reset(); + + struct Statistics { + uint64_t total; + uint64_t current; + uint64_t cached; + uint64_t max; + }; + + static Statistics statistics(); + +private: + friend void ::_Trampoline(unsigned int y, unsigned int x); + enum class State { Init, Running, Aborting, Yielded, Idle, Finished }; + + /** Code to run just before we switch to a fiber. */ + void _startSwitchFiber(const char* tag, const void* stack_bottom = nullptr, size_t stack_size = 0); + + /** Code to run just after we have switched to a fiber. */ + void _finishSwitchFiber(const char* tag); + + State _state{State::Init}; + std::optional> _function; + std::optional _result; + std::exception_ptr _exception; + + ucontext_t _uctx{}; + jmp_buf _fiber{}; + jmp_buf _trampoline{}; + jmp_buf _parent{}; + +#ifdef HILTI_HAVE_SANITIZER + struct { + const void* prev_bottom = nullptr; + size_t prev_size = 0; + void* fake_stack = nullptr; + } _asan; +#endif + + inline static uint64_t _total_fibers; + inline static uint64_t _current_fibers; + inline static uint64_t _max_fibers; +}; + +extern void yield(); + +} // namespace detail + +/** + * Executor for a function that may yield control back to the caller even + * before it's finished. The caller can then later resume the function to + * continue its operation. + */ +class Resumable { +public: + /** + * Creates an instance initialied with a function to execute. The + * function can then be started by calling `run()`. + * + * @param f function to be executed + */ + template::value>> + Resumable(Function f) { + _fiber = detail::Fiber::create(); + + std::function x = [f](resumable::Handle* r) -> std::any { + using R = decltype(f(static_cast(nullptr))); + if constexpr ( std::is_same::value ) { + f(r); + return true; + } + else // NOLINT + return f(r); + }; + + _fiber->init(std::move(x)); + } + + Resumable(Resumable&& r) noexcept : _fiber(std::move(r._fiber)), _result(std::move(r._result)) {} + + Resumable& operator=(Resumable&& other) noexcept { + _fiber = std::move(other._fiber); + _result = std::move(other._result); + return *this; + } + + Resumable() = default; + Resumable(const Resumable& r) = delete; + Resumable& operator=(const Resumable& other) = delete; + + ~Resumable() { + if ( _fiber ) + detail::Fiber::destroy(std::move(_fiber)); + } + + /** Starts execution of the function. This must be called only once. */ + void run(); + + /** When a function has yielded, resumes its operation. */ + void resume(); + + /** When a function has yielded, abort its operation without resuming. */ + void abort(); + + /** Returns a handle to the currently running function. */ + resumable::Handle* handle() { return _fiber.get(); } + + /** + * Returns the function's result once it has completed. Must not be + * called before completion; check with `operator bool()` first. + */ + template + Result get() const { + assert(static_cast(_result)); + + if constexpr ( std::is_same::value ) + return; + + return std::any_cast(*_result); + } + + /** + * Returns true if the function has completed. If so, `get()` can be + * called to retrieve its result. + */ + operator bool() const { return static_cast(_result); } + +private: + void yielded(); + + void checkFiber(const char* location) const { + if ( ! _fiber ) + throw std::logic_error(std::string("fiber not set in ") + location); + } + + std::unique_ptr _fiber; + std::optional _result; +}; + +namespace fiber { + +/** + * Executes a resumable function. This is a utility wrapper around + * `Resumable` that immediately starts the function. + */ +template +auto execute(Function f) { + Resumable r(std::move(f)); + r.run(); + return r; +} + +} // namespace fiber +} // namespace hilti::rt diff --git a/hilti/include/rt/fmt.h b/hilti/include/rt/fmt.h new file mode 100644 index 000000000..85c647b2f --- /dev/null +++ b/hilti/include/rt/fmt.h @@ -0,0 +1,20 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti::rt { + +/** sprintf-style string formatting. */ +template +std::string fmt(const char* fmt, const Args&... args) { + return tfm::format(fmt, args...); +} + +/** sprintf-style string formatting. */ +template +std::string fmt(const std::string& s, const Args&... args) { + return tfm::format(s.c_str(), args...); +} +} // namespace hilti::rt diff --git a/hilti/include/rt/global-state.h b/hilti/include/rt/global-state.h new file mode 100644 index 000000000..d35278d4b --- /dev/null +++ b/hilti/include/rt/global-state.h @@ -0,0 +1,126 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +// We collect all (or most) of the runtime's global state centrally. That's +// 1st good to see what we have (global state should be minimal) and 2nd +// helpful to ensure that JIT maps things correctly. Note that all code +// accessing any of this state is in charge of ensuring thread-safety itself. +// These globals are generally initialized through hilti::rt::init(); +// +// TODO(robin): Accesses to global state are *not* completely thread-safe yet. + +namespace hilti::rt { +struct Configuration; + +namespace detail { +class DebugLogger; +} + +} // namespace hilti::rt + +namespace hilti::rt::detail { + +/** Struct capturing all truely global runtime state. */ +struct GlobalState { + GlobalState() = default; + ~GlobalState(); + + GlobalState(const GlobalState&) = delete; + GlobalState(GlobalState&&) = delete; + GlobalState& operator=(const GlobalState&) = delete; + GlobalState& operator=(GlobalState&&) = delete; + + /** True once `hilit::init()`` has finished. */ + bool runtime_is_initialized = false; + + /** If not zero, `Configuration::abort_on_exception` is disabled. */ + int disable_abort_on_exceptions = 0; + + /** The runtime's configuration. */ + std::unique_ptr configuration; + + /** Debug logger recording runtime diagnostics. */ + std::unique_ptr debug_logger; + + /** The context for the main thread. */ + std::unique_ptr master_context; + + /** Cache of previously used fibers available for reuse. */ + std::vector> fiber_cache; + + /** + * If non-null, a string descriging the most recent source code position. + * use `debug::setLocation()` to set. + */ + const char* source_location{}; + + /** + * List of HILTI modules registered with the runtime. This is filled through `registerModule()`, which in turn gets + * called through a module's global constructors at initialization time. + * + * @note Must come last in this struct as destroying other fields may + * still need this information. + */ + std::vector hilti_modules; +}; + +/** + * Pointer to the global state singleton. Do not access directly, use + * `globalState()` instead. + */ +extern GlobalState* __global_state; + +/** Creates the global state singleton. */ +extern GlobalState* createGlobalState(); + +/** + * Returns the global state singleton. This creates the state the first time + * it's called. + */ +inline auto globalState() { + if ( __global_state ) + return __global_state; + + return createGlobalState(); +} + +/** Returns the current context's array of HILTI global variables. */ +inline auto hiltiGlobals() { + assert(context::detail::current()); + return context::detail::current()->hilti_globals; +} + +/** + * Returns the current context's set of a HILTI module's global variables. + * + * @param idx module's index inside the array of HILTI global variables; + * this is determined by the HILTI linker + */ +template +inline auto moduleGlobals(int idx) { + return std::static_pointer_cast(hiltiGlobals()[idx]); +} + +/** + * Initialized the current context's set of a HILTI module's global + * variables. + * + * @param idx module's index inside the array of HILTI global variables; + * this is determined by the HILTI linker + */ +template +inline auto initModuleGlobals(unsigned int idx) { + if ( context::detail::current()->hilti_globals.size() <= idx ) + context::detail::current()->hilti_globals.resize(idx + 1); + + context::detail::current()->hilti_globals[idx] = std::make_shared(); +} + +} // namespace hilti::rt::detail diff --git a/hilti/include/rt/hilti.h b/hilti/include/rt/hilti.h new file mode 100644 index 000000000..d23f27573 --- /dev/null +++ b/hilti/include/rt/hilti.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * This header defines functions that are made available to HILTI programs + * inside the `hilti::*` namespace. + */ + +#pragma once + +#include + +#include +#include + +namespace hilti::rt { + +/** Corresponds to `hilti::print`. */ +template +void print(const T& t, bool newline = true) { + std::cout << hilti::rt::to_string_for_print(t); + + if ( newline ) + std::cout << std::endl; + else + std::cout.flush(); +} + +/** Corresponds to `hilti::printValues`. */ +template::value>* = nullptr> +void printValues(const T& t, bool newline = true) { + std::cout << join_tuple_for_print(t); + + if ( newline ) + std::cout << std::endl; + else + std::cout.flush(); +} + +// Just for testing: Declaring a function that's not implementd. +extern void __does_not_exist(); + +}; // namespace hilti::rt diff --git a/hilti/include/rt/init.h b/hilti/include/rt/init.h new file mode 100644 index 000000000..304378550 --- /dev/null +++ b/hilti/include/rt/init.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +namespace hilti::rt { + +struct Context; + +/** + * Initializes the HILTI run-time library. This must be called once at + * startup before any other libhilti functionality can be used. The one + * exception to that rule `hlt_set_config()`, which can (and must) be called + * before hlt_init(). + */ +extern void init(); + +/** + * Shuts down the run-time library, freeing all resources. Once executed, no + * libhilti functioality can be used anymore. + */ +extern void done(); + +/** Returns true if init() has already been called. */ +extern bool isInitialized(); + +namespace detail { + +/** A HILTI module registered with the runtime. The HILTI code generator creates code to register an instance of this + * for every module it compiles. */ +struct HiltiModule { + const char* name; /**< name of the HILTI module; for informational purposes */ + void (*init_module)(); /**< callback for executing any top-level module code when the runtime library is being + initialized */ + void (*init_globals)( + hilti::rt::Context* ctx); /**< callback to initialize the module's globals in a freshly allocatec context */ + unsigned int* + globals_idx; /**< pointer to an integer storing the modules' index in the context-wide globals array */ +}; + +/** Entry point for the generated code to register a compiled HILTI module with the runtime */ +extern void registerModule(HiltiModule module); + +/** Macro to schedule a global function to be called at startup time through a global constructor. */ +#define HILTI_PRE_INIT(func) static ::hilti::rt::detail::ExecutePreInit __pre_init_##__COUNTER__(func); + +/** Helper class to execute a global function at startup time through a global constructor. */ +class ExecutePreInit { +public: + ExecutePreInit(void (*f)()) { (*f)(); } +}; + +} // namespace detail + +} // namespace hilti::rt diff --git a/hilti/include/rt/iterator.h b/hilti/include/rt/iterator.h new file mode 100644 index 000000000..14521a1c7 --- /dev/null +++ b/hilti/include/rt/iterator.h @@ -0,0 +1,172 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace hilti::rt { + +/** Exception flagging access to an interator that not, or no longer, valid. */ +HILTI_EXCEPTION(InvalidIterator, RuntimeError) + +namespace detail::iterator { + +struct ControlBlock {}; + +// Base class for containers providing safe iterators. +class Controllee { +public: + ~Controllee() { _control.reset(); } + Controllee() = default; + Controllee(const Controllee& /*unused*/){}; + Controllee(Controllee&& /*unused*/) noexcept {}; + Controllee& operator=(const Controllee& /*unused*/) { + _control.reset(); + return *this; + }; + Controllee& operator=(Controllee&& /*unused*/) noexcept { + _control.reset(); + return *this; + }; + + std::weak_ptr control() const { + if ( ! _control ) + _control = std::make_shared(); + + return _control; + } + +private: + mutable std::shared_ptr _control; +}; + +template +class SafeIterator { +public: + SafeIterator() = default; + SafeIterator(const C& c, const I& i) : _i(std::move(i)), _control(c.control()) {} + SafeIterator(std::weak_ptr control, const I& i) + : _i(std::move(i)), _control(std::move(control)) {} + SafeIterator(const SI& s) : _i(s._i), _control(s.control()) {} + + auto& operator*() const { + ensureValid(); + return *_i; + } + bool operator==(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i == other._i; + } + bool operator!=(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i != other._i; + } + bool operator<(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i < other._i; + } + bool operator<=(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i <= other._i; + } + bool operator>(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i > other._i; + } + bool operator>=(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i >= other._i; + } + auto& operator++() { + ensureValid(); + ++_i; + return static_cast(*this); + } + auto operator++(int) { // NOLINT + ensureValid(); + auto tmp = *this; + ++_i; + return static_cast(tmp); + } + auto& operator--() { + ensureValid(); + return --_i; + return static_cast(*this); + } + auto operator--(int) { // NOLINT + ensureValid(); + auto tmp = *this; + ++_i; + return static_cast(tmp); + } + + auto operator-(const SafeIterator& other) const { + ensureValid(); + ensureSame(other); + return _i - other._i; + } + + template + auto operator+(const T& n) const { + ensureValid(); + return SI(_control, _i + n); + } + + template + auto& operator+=(const T& n) { + ensureValid(); + _i += n; + return *this; + } + + explicit operator bool() const { return ! _control.expired(); } + +protected: + void ensureValid() const { + if ( _control.expired() ) + throw InvalidIterator("bound object has expired"); + } + + void ensureSame(const SafeIterator& other) const { + if ( _control.lock() != other._control.lock() ) + throw InvalidIterator("iterators refer to differen objects"); + } + +private: + I _i; + std::weak_ptr _control; +}; + +/** Proxy class returned by `safe_range`. */ +template +class SafeRange { +public: + SafeRange(const T& t) : _t(t) {} + auto begin() const { return safe_begin(_t); } + auto end() const { return safe_end(_t); } + +private: + const T& _t; +}; + +} // namespace detail::iterator + +/** + * Wrapper around safe_begin/safe_end that returns an object suitable to + * operate range-based for loop on to iterator over a sequence. + */ +template +auto safe_range(const T& t) { + return detail::iterator::SafeRange(t); +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/libhilti.h b/hilti/include/rt/libhilti.h new file mode 100644 index 000000000..01b247ffd --- /dev/null +++ b/hilti/include/rt/libhilti.h @@ -0,0 +1,20 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace hilti::rt::bytes::literals; // NOLINT (google-global-names-in-headers) diff --git a/hilti/include/rt/logging.h b/hilti/include/rt/logging.h new file mode 100644 index 000000000..3bf8d9ce6 --- /dev/null +++ b/hilti/include/rt/logging.h @@ -0,0 +1,88 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace hilti::rt { + +/** Reports an internal error and aborts execution. */ +// void internalError(const std::string& msg) __attribute__((noreturn)); // Declared in util.h + +/** Reports a fatal error and aborts execution. */ +void fatalError(const std::string& msg) __attribute__((noreturn)); + +/** Reports a warning. */ +void warning(const std::string& msg); + +/** + * Prints a string, or a runtime value, to a specific debug stream. This is a + * macro wrapper around `debug::detail::print(*)` that avoids evaluation of + * the arguments if nothing is going to get logged. + */ +#define HILTI_RT_DEBUG(stream, msg) \ + { \ + if ( ::hilti::rt::detail::globalState()->debug_logger && \ + ::hilti::rt::detail::globalState()->debug_logger->isEnabled(stream) ) \ + ::hilti::rt::debug::detail::print(stream, msg); \ + } + +namespace debug { + +namespace detail { +/** Prints a debug message to a specific debug stream. */ +inline void print(const std::string& stream, const char* msg) { + if ( ::hilti::rt::detail::globalState()->debug_logger ) + ::hilti::rt::detail::globalState()->debug_logger->print(stream, msg); +} + +/** Print a string to a specific debug stream with proper escaping. */ +inline void print(const std::string& stream, const std::string_view& s) { + if ( ::hilti::rt::detail::globalState()->debug_logger ) + ::hilti::rt::detail::globalState()->debug_logger->print(stream, hilti::rt::escapeBytes(s, false, true)); +} + +template::value>* = nullptr> +/** Prints the string representastion of a HILTI runtime value to a specific debug stream. */ +inline void print(const std::string& stream, const T& t) { + if ( ::hilti::rt::detail::globalState()->debug_logger ) + ::hilti::rt::detail::globalState()->debug_logger->print(stream, hilti::rt::to_string_for_print(t)); +} +} // namespace detail + +/** Returns true if debug loggin is enabled for a given stream. */ +inline bool isEnabled(const std::string& stream) { + return ::hilti::rt::detail::globalState()->debug_logger && + ::hilti::rt::detail::globalState()->debug_logger->isEnabled(stream); +} + +/** Increases the indentation level for a debug stream. */ +inline void indent(const std::string& stream) { + if ( ::hilti::rt::detail::globalState()->debug_logger ) + ::hilti::rt::detail::globalState()->debug_logger->indent(stream); +} + +/** Decreases the indentation level for a debug stream. */ +inline void dedent(const std::string& stream) { + if ( ::hilti::rt::detail::globalState()->debug_logger ) + ::hilti::rt::detail::globalState()->debug_logger->dedent(stream); +} + +/** + * Returns the current source code location if set, or null if not. + */ +inline const char* location() { return ::hilti::rt::detail::globalState()->source_location; } + +/** + * Sets the current source code location; or unsets if no argumet. + * *loc* must point to a static string that won't go out of scope. + */ +inline void setLocation(const char* l = nullptr) { ::hilti::rt::detail::globalState()->source_location = l; } + +} // namespace debug +} // namespace hilti::rt diff --git a/hilti/include/rt/result.h b/hilti/include/rt/result.h new file mode 100644 index 000000000..70a1e1688 --- /dev/null +++ b/hilti/include/rt/result.h @@ -0,0 +1,168 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +namespace hilti::rt { + +namespace result { + +/** Represents an error message. */ +class Error { +public: + Error(std::string description = "") : _description(std::move(description)) {} + auto& description() const { return _description; } + operator std::string() const { return _description; } + operator std::string_view() const { return _description; } + +private: + std::string _description; +}; + +inline std::ostream& operator<<(std::ostream& out, const Error& error) { + out << error.description(); + return out; +} + +inline bool operator==(const Error& a, const Error& b) { return a.description() == b.description(); } +inline bool operator!=(const Error& a, const Error& b) { return ! (a == b); } + +/** Exception indicating that no result is available if though one was requested. */ +class NoResult : public RuntimeError { +public: + NoResult(Error err) : RuntimeError(err.description()), _error(std::move(err)) {} + +private: + Error _error; +}; + +/** Exception indicating that no error has been reported if though one was expected to be available. */ +class NoError : public RuntimeError { +public: + NoError() : RuntimeError("") {} +}; + +} // namespace result + +struct Nothing {}; + +/** + * Represents either a successful result from function if it returned one, or + * reflects an error if the function was unsuccesful. + */ +template +class Result { +public: + Result() : _value(result::Error("")) {} + + /** Creates a successful result from a value. */ + Result(const T& t) : _value(t) {} + /** Creates a successful result from a value. */ + Result(T&& t) : _value(std::move(t)) {} + /** Creates an result reflecting an error. */ + Result(const result::Error& e) : _value(e) {} + /** Creates an result reflecting an error. */ + Result(result::Error&& e) : _value(std::move(e)) {} + + Result(const Result& o) = default; + Result(Result&& o) = default; // NOLINT (hicpp-noexcept-move) + ~Result() = default; + + /** + * Returns the result's value, assuming it indicates success. + * + * @exception `std::bad_variant_access` if the result reflects an error + * state + */ + const T& value() const { return std::get(_value); } + + /** + * Returns the result's value, assuming it indicates success. + * + * @exception `std::bad_variant_access` if the result reflects an error + * state + */ + T& value() { return std::get(_value); } + + /** + * Returns the result's value if it indicates success, or throws an + * exception if not. + * + * @exception `result::NoResult` if the result reflects an error state + */ + const T& valueOrThrow() const { + if ( ! hasValue() ) + throw result::NoResult(error()); + + return value(); + } + + /** + * Returns the result's value if it indicates success, or throws an + * exception if not. + * + * @exception `result::NoResult` if the result reflects an error state + */ + T& valueOrThrow() { + if ( ! hasValue() ) + throw result::NoResult(error()); + + return value(); + } + + /** + * Returns the result's error, assuming it reflect one. + * + * @exception `std::bad_variant_access` if the result doe not reflect an error + * state + */ + const result::Error& error() const { return std::get(_value); } + + /** + * Returns the result's error if it indicates failure, or throws an + * exception if not. + * + * @exception `result::NoError` if the result does not reflect an error state + */ + const result::Error& errorOrThrow() const { + if ( hasValue() ) + throw result::NoError(); + + return error(); + } + + /** Returns true if the result represents a succesful return value. */ + bool hasValue() const { return std::holds_alternative(_value); } + + /** Returns the result's value, assuming it indicates success. */ + const T& operator*() const { return value(); } + /** Returns the result's value, assuming it indicates success. */ + T& operator*() { return value(); } + /** Returns the result's value, assuming it indicates success. */ + const T* operator->() const { return std::get_if(&_value); } + /** Returns the result's value, assuming it indicates success. */ + T* operator->() { return std::get_if(&_value); } + + /** Returns true if the result represents a succesful return value. */ + operator bool() const { return hasValue(); } + + /** Converts the result to an optional that's set if it represents a succesful return value. */ + operator std::optional() const { return hasValue() ? std::make_optional(value()) : std::nullopt; } + + Result& operator=(const Result& other) = default; + Result& operator=(Result&& other) = default; // NOLINT (hicpp-noexcept-move) + +private: + std::variant _value; +}; + +} // namespace hilti::rt diff --git a/hilti/include/rt/safe-int.h b/hilti/include/rt/safe-int.h new file mode 100644 index 000000000..bacb9d1f3 --- /dev/null +++ b/hilti/include/rt/safe-int.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace hilti::rt { + +/** + * Exception triggered when a numerical operation causes an overflow. + */ +HILTI_EXCEPTION(Overflow, RuntimeError) + +/** + * Exception triggered when a division by zero is attempted. + */ +HILTI_EXCEPTION(DivisionByZero, RuntimeError) + +namespace integer { + +namespace detail { +class SafeIntException { +public: + static void SafeIntOnOverflow() __attribute__((noreturn)) { throw Overflow("integer overflow"); } + + static void SafeIntOnDivZero() __attribute__((noreturn)) { throw DivisionByZero("integer devision by zero"); } +}; +} // namespace detail + +template +using safe = SafeInt; + +} // namespace integer +} // namespace hilti::rt + +// Needs to be a top level. +template +inline auto operator<<(O& out, const hilti::rt::integer::safe& x) + -> std::enable_if_t, O>& { + if ( std::is_same() ) + out << static_cast(x); + else if ( std::is_same() ) + out << static_cast(x); + else + out << x.Ref(); + + return out; +} diff --git a/hilti/include/rt/threading.h b/hilti/include/rt/threading.h new file mode 100644 index 000000000..763650dca --- /dev/null +++ b/hilti/include/rt/threading.h @@ -0,0 +1,19 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti::rt { + +namespace vthread { + +/** Type for the unique IDs of virtual threads. */ +using ID = int64_t; + +/** ID of original (main) thread. */ +inline const ID Master = -1; + +} // namespace vthread + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/address.h b/hilti/include/rt/types/address.h new file mode 100644 index 000000000..67405196c --- /dev/null +++ b/hilti/include/rt/types/address.h @@ -0,0 +1,124 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace hilti::rt { + +enum class AddressFamily { Undef, IPv4, IPv6 }; + +/** + * Represents HILTI address type. This treats IPv4 and IPv6 addresses + * transparently by internally embedding the former into the latter's space. + */ +class Address { +public: + /** + * Constructs an address from a IPv4 or IPv6 string representation. + * + * @param addr string representation, as in `1.2.3.4` or `2001:db8:85a3:8d3:1319:8a2e:370:7348`. + * + * @throws RuntimeError if it cannot parse the address into a valid IPv4 or IPv6 address. + */ + explicit Address(const std::string& addr) { _parse(addr); } + + /** Constructs an address from a C `in_addr` struct. */ + explicit Address(struct in_addr addr4) { _init(addr4); } + + /** + * Constructs an address from a C `in6_addr` struct. + */ + explicit Address(struct in6_addr addr6) { _init(addr6); } + + /** + * Constructs an address from binary representation of an IPv4 address. + * + * @param addr4 IPv4 address in host byte order + */ + explicit Address(uint32_t addr4) : Address(0, addr4) {} // addr4 in host byte order + + /** + * Constructs an address from binary representation of an IPv6 address. + * + * @param addr6a upper bits of IPv6 address in host byte order + * @param addr6a lower bits of IPv6 address in host byte order + */ + explicit Address(uint64_t addr6a, uint64_t addr6b) : _a1(addr6a), _a2(addr6b){}; + + Address() noexcept = default; + Address(const Address&) = default; + Address(Address&&) noexcept = default; + ~Address() = default; + + Address& operator=(const Address&) = default; + Address& operator=(Address&&) noexcept = default; + + /** + * Returns the address family of the address, which can be either IPv4 or + * IPv6. + */ + AddressFamily family() const; + + /** + * Returns a network prefix by masking out lower bits of the address. + * + * @param width number of upper bits to keep. + */ + Address mask(unsigned int width) const; + + /** + * Returns the address as `in{,6}_addr` depending on whether it's a v4 or + * v6 value. For an unset address, returns an IPv4 `0.0.0.0`. + */ + std::variant asInAddr() const; + + bool operator==(const Address& other) const; + bool operator!=(const Address& other) const { return ! (*this == other); } + + /** + * Returns a string representation of the address. For addresses in the + * IPv4 space, this will returns the standard IPv4 notation, whereas IPv6 + * addresses will be formatted as such. The returned format corresponds + * to what the corresponding constructor parses. + */ + operator std::string() const; + +private: + void _init(struct in_addr addr); + void _init(struct in6_addr addr); + + // Throws RuntimeError if it cannot parse the address. + void _parse(const std::string& addr); + + uint64_t _a1 = 0; // The 8 more significant bytes. + uint64_t _a2 = 0; // The 8 less significant bytes. +}; + +namespace address { +/** Unpacks an address from binary representation, following the protocol for `unpack` operator. */ +extern Result> unpack(const Bytes& data, AddressFamily family, ByteOrder fmt); + +/** Unpacks an address from binary representation, following the protocol for `unpack` operator. */ +extern Result> unpack(const stream::View& data, AddressFamily family, ByteOrder fmt); + +} // namespace address + +namespace detail::adl { +extern std::string to_string(const AddressFamily& x, adl::tag /*unused*/); +inline std::string to_string(const Address& x, adl::tag /*unused*/) { return x; } +} // namespace detail::adl + +inline std::ostream& operator<<(std::ostream& out, const Address& x) { + out << to_string(x); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const AddressFamily& family) { return out << to_string(family); } + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/all.h b/hilti/include/rt/types/all.h new file mode 100644 index 000000000..29218a0b9 --- /dev/null +++ b/hilti/include/rt/types/all.h @@ -0,0 +1,28 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/hilti/include/rt/types/bool.h b/hilti/include/rt/types/bool.h new file mode 100644 index 000000000..d70a94be8 --- /dev/null +++ b/hilti/include/rt/types/bool.h @@ -0,0 +1,14 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace hilti::rt { + +namespace detail::adl { +inline std::string to_string(bool x, adl::tag /*unused*/) { return (x ? "True" : "False"); } + +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/bytes.h b/hilti/include/rt/types/bytes.h new file mode 100644 index 000000000..d5b01dade --- /dev/null +++ b/hilti/include/rt/types/bytes.h @@ -0,0 +1,378 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti::rt { + +class Bytes; +class RegExp; + +namespace bytes { + +/** For Bytes::Strip, which side to strip from. */ +enum class Side { + Left, /**< left side */ + Right, /**< right side */ + Both /**< left and right sides */ +}; + +/** For Bytes::Decode, which character set to use. */ +enum class Charset { Undef, UTF8, ASCII }; + +/** TODO: Remove */ +class SafeIterator : public hilti::rt::detail::iterator::SafeIterator { +public: + using Base = hilti::rt::detail::iterator::SafeIterator; + using Base::Base; +}; + +class SafeConstIterator + : public hilti::rt::detail::iterator::SafeIterator { +public: + using Base = hilti::rt::detail::iterator::SafeIterator; + using Base::Base; + + template + auto& operator+=(const hilti::rt::integer::safe& n) { + return Base::operator+=(n.Ref()); + } + + auto& operator+=(uint64_t n) { return Base::operator+=(n); } + + template + auto operator+(const hilti::rt::integer::safe& n) const { + return Base::operator+(n.Ref()); + } + + template + auto operator+(const T& n) const { + return Base::operator+(n); + } +}; + +inline std::string to_string(const SafeConstIterator& /* i */, detail::adl::tag /*unused*/) { + return ""; +} + +inline std::ostream& operator<<(std::ostream& out, const SafeConstIterator& /* x */) { + out << ""; + return out; +} + +} // namespace bytes + +/** + * HILTI's bytes instance, built on top of a std::string. + */ +class Bytes : public std::string, public hilti::rt::detail::iterator::Controllee { +public: + using Base = std::string; + using Iterator = bytes::SafeConstIterator; + using Offset = uint64_t; + + using Base::Base; + + /** + * Creates a bytes instance from a UTF8 string, transforming the contents + * into a binary representation defined by a specified character set. + * + * @param s string assumed to be in UTF8 (as all runtime strings) + * @param cs character set to use for the binary encoding + * @return bytes instances encoding *s* in character set *cs* + */ + Bytes(std::string s, bytes::Charset cs); + + Bytes(Base&& str) : Base(std::move(str)) {} + Bytes(const Bytes&) = default; + Bytes(Bytes&&) noexcept = default; + ~Bytes() = default; + + Bytes& operator=(const Bytes&) = default; + Bytes& operator=(Bytes&&) noexcept = default; + + /** Appends the contents of a stream view to the data. */ + void append(const Bytes& d) { Base::append(d.str()); } + + /** Appends the contents of a stream view to the data. */ + void append(const stream::View& view) { Base::append(view.data()); } + + /** Returns the bytes' data as a string instance. */ + const std::string& str() const { return *this; } + + /** Returns an interator representing the first byte of the instance. */ + Iterator safeBegin() const { return {*this, Base::begin()}; } + + /** Returns an interator representing the end of the instance. */ + Iterator safeEnd() const { return Iterator(*this, Base::end()); } + + /** Returns an iterator refering to the given offset. */ + Iterator at(Offset o) const { return safeBegin() + o; } + + /** Returns true if the data's size is zero. */ + bool isEmpty() const { return empty(); } + + /** Returns the size of instance in bytes. */ + int64_t size() const { return static_cast(std::string::size()); } + + /** + * Returns the position of the first occurence of a byte. + * + * @param b byte to search + * @param n optional starting point, which must be inside the same instance + */ + Iterator find(value_type b, const Iterator& n = Iterator()) const { + if ( auto i = Base::find(b, (n ? n - safeBegin() : 0)); i != Base::npos ) + return safeBegin() + i; + else + return safeEnd(); + } + + /** + * Returns the position of the first occurence of a range of bytes + * + * @param v bytes to search + * @param n optional starting point, which must be inside the same instance + * @return tuple where the 1st element is a boolean indicating whether + * *v* has been found; if yes, the 2nd element points to the 1st bytes; + * if no, the 2nd element points to the first byte so that no earlier + * position has even a partial match of *v*. + */ + std::tuple find(const Bytes& v, const Iterator& n = Iterator()) const; + + /** + * Extracts a subrange of bytes. + * + * @param from iterator pointing to start of subrange + * @param to iterator pointing to just beyond subrange + */ + Bytes sub(const Iterator& from, const Iterator& to) const { return substr(from - safeBegin(), to - from); } + + /** + * Extracts a subrange of bytes from the beginning. + * + * @param to iterator pointing to just beyond subrange + */ + Bytes sub(const Iterator& to) const { return sub(safeBegin(), to); } + + /** + * Extracts a subrange of bytes. + * + * @param offset of start of subrage + * @param offset of one byeond end of subrage + */ + Bytes sub(Offset from, Offset to) const { return substr(from, to - from); } + + /** + * Extracts a subrange of bytes from the beginning. + * + * @param to offset of one beyond end of subrange + */ + Bytes sub(Offset to) const { return sub(0, to); } + + /** + * Extracts a fixed number of bytes from the data + * + * @tparam N number of bytes to extract + * @param dst array to writes bytes into + * @return new bytes instance that has the first *N* bytes removed. + */ + template + Bytes extract(unsigned char (&dst)[N]) const { + memcpy(dst, data(), N); + return sub(N, std::string::npos); + } + + /** + * Decodes the binary data into a string assuming its encoded in a + * specified character set. + * + * @param cs character set to assume the binary data to be encoded in + * @return UTF8 string + */ + std::string decode(bytes::Charset cs) const; + + /** Returns true if the data begins with a given, other bytes instance. */ + bool startsWith(const Bytes& b) const { return hilti::rt::startsWith(*this, b); } + + /** + * Returns an upper-case version of the instance. This internally first + * decodes the data assuming a specified character set, then encodes it + * back afterwards. + * + * @param cs character set for decoding/encoding + */ + Bytes upper(bytes::Charset cs) const { return Bytes(hilti::rt::string::upper(decode(cs)), cs); } + + /** Returns an upper-case version of the instance. */ + Bytes lower(bytes::Charset cs) const { return Bytes(hilti::rt::string::lower(decode(cs)), cs); } + + /** + * Removes leading and/or trailing sequences of all characters of a set + * from the bytes instance. + * + * @param side side of bytes instace to be stripped. + * @param set characters to remove; removes all whitespace if empty + */ + Bytes strip(const Bytes& set, bytes::Side side = bytes::Side::Both) const; + + /** + * Removes leading and/or trailing sequences of white space from the + * bytes instance. + * + * @param side side of bytes instace to be stripped. + */ + Bytes strip(bytes::Side side = bytes::Side::Both) const; + + /** Splits the data at sequences of whitespace, returning the parts. */ + Vector split() { + Vector x; + for ( auto& v : hilti::rt::split(*this) ) + x.push_back(Bytes(v)); + return x; + } + + /** + * Splits the data (only) at the first sequence of whitespace, returning + * the two parts. + */ + std::tuple split1() const { + auto p = hilti::rt::split1(str()); + return std::make_tuple(p.first, p.second); + } + + /** Splits the data at occurences of a separator, returning the parts. */ + Vector split(const Bytes& sep) const { + Vector x; + for ( auto& v : hilti::rt::split(*this, sep) ) + x.push_back(Bytes(v)); + return x; + } + + /** + * Splits the data (only) at the first occurance of a separator, + * returning the two parts. + */ + std::tuple split1(const Bytes& sep) const { + auto p = hilti::rt::split1(str(), sep); + return std::make_tuple(p.first, p.second); + } + + /** + * Returns the concatenation of all elements in the *parts* list rendered + * as printable strings and separated by the bytes value providing this + * method. + */ + template + Bytes join(const std::vector& parts) const { + Bytes rval; + + for ( auto i = 0; i < parts.size(); ++i ) { + if ( i > 0 ) + rval += *this; + + rval += Bytes(hilti::rt::to_string_for_print(parts[i]).data()); + } + + return rval; + } + + /** + * Interprets the data as an ASCII representation of a signed integer and + * extracts that. + * + * @param base base to use for conversion + * @return converted integer value + */ + int64_t toInt(uint64_t base = 10) const; + + /** + * Interprets the data as an ASCII representation of an unsigned integer + * and extracts that. + * + * @param base base to use for conversion + * @return converted integer value + */ + uint64_t toUInt(uint64_t base = 10) const; + + /** + * Interprets the data as an binary representation of a signed integer + * and extracts that. + * + * @param byte_order byte order that the integer is encoded in + * @return converted integer value + */ + int64_t toInt(hilti::rt::ByteOrder byte_order) const; + + /** + * Interprets the data as an binary representation of an unsigned + * integer and extracts that. + * + * @param byte_order byte order that the integer is encoded in + * @return converted integer value + */ + uint64_t toUInt(hilti::rt::ByteOrder byte_order) const; + + /** + * Interprets the data as an ASCII representation of a integer value + * representing seconds since the UNIX epoch, and extracts that. + * + * @param base base to use for conversion + * @return converted time value + */ + Time toTime(uint64_t base = 10) const { return Time(toUInt(base) * 1000000000); } + + /** + * Interprets the data as an binary representation of a integer value + * representing seconds since the UNIX epoch, and extracts that. + * + * @param base base to use for conversion + * @return converted time value + */ + Time toTime(hilti::rt::ByteOrder byte_order) const { return Time(toUInt(byte_order) * 1000000000); } + + /** + * Matchs the data against a regular expression. + * + * @param re compiled regular expression + * @param group capture group to return + * @return the matching group, or unset if no match + */ + Result match(const RegExp& re, unsigned int group = 0); +}; + +inline std::ostream& operator<<(std::ostream& out, const Bytes& x) { + out << x.str(); + return out; +} + +namespace bytes { +inline namespace literals { +inline Bytes operator"" _b(const char* str, size_t size) { return Bytes(str, size); } +} // namespace literals +} // namespace bytes + +template<> +inline std::string detail::to_string_for_print(const Bytes& x) { + return escapeBytes(x.str(), false, true); +} + +namespace detail::adl { +inline auto safe_begin(const Bytes& x, adl::tag /*unused*/) { return x.safeBegin(); } +inline auto safe_end(const Bytes& x, adl::tag /*unused*/) { return x.safeEnd(); } +inline std::string to_string(const Bytes& x, adl::tag /*unused*/) { return fmt("b\"%s\"", escapeBytes(x.str(), true)); } +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/error.h b/hilti/include/rt/types/error.h new file mode 100644 index 000000000..48e416cab --- /dev/null +++ b/hilti/include/rt/types/error.h @@ -0,0 +1,21 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti::rt { + +namespace detail::adl { +inline std::string to_string(const result::Error& x, adl::tag /*unused*/) { + if ( ! x.description().empty() ) + return fmt("", x.description()); + + return ""; +} + +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/integer.h b/hilti/include/rt/types/integer.h new file mode 100644 index 000000000..a765d0d04 --- /dev/null +++ b/hilti/include/rt/types/integer.h @@ -0,0 +1,257 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace hilti::rt { + +namespace detail::adl { +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRIu64, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRId64, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRIu32, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRId32, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRIu16, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRId16, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRIu8, *x.Ptr()); +} + +inline std::string to_string(hilti::rt::integer::safe x, adl::tag /*unused*/) { + return fmt("%" PRId8, *x.Ptr()); +} + +inline std::string to_string(uint64_t x, adl::tag /*unused*/) { return fmt("%" PRIu64, x); } + +inline std::string to_string(int64_t x, adl::tag /*unused*/) { return fmt("%" PRId64, x); } + +inline std::string to_string(uint32_t x, adl::tag /*unused*/) { return fmt("%" PRIu32, x); } + +inline std::string to_string(int32_t x, adl::tag /*unused*/) { return fmt("%" PRId32, x); } + +inline std::string to_string(uint16_t x, adl::tag /*unused*/) { return fmt("%" PRIu16, x); } + +inline std::string to_string(int16_t x, adl::tag /*unused*/) { return fmt("%" PRId16, x); } + +inline std::string to_string(uint8_t x, adl::tag /*unused*/) { return fmt("%" PRIu8, x); } + +inline std::string to_string(int8_t x, adl::tag /*unused*/) { return fmt("%" PRId8, x); } + +} // namespace detail::adl + +namespace integer { +namespace detail { + +template +inline Result, D>> unpack(D b, const uint8_t* dst, std::initializer_list bytes) { + T x = 0; + for ( auto i : bytes ) { + x <<= 8U; + x |= (static_cast(dst[i])); + } + + return std::make_tuple(static_cast>(x), std::move(b)); +} + +} // namespace detail + +template +inline Result, D>> unpack(D b, ByteOrder fmt) { + if ( fmt == ByteOrder::Host ) + return unpack(b, systemByteOrder()); + + if ( b.size() < static_cast(sizeof(T)) ) + return result::Error("insufficient data to unpack integer"); + + uint8_t raw[sizeof(T)]; + b = b.extract(raw); + + switch ( fmt ) { + case ByteOrder::Big: + case ByteOrder::Network: + if constexpr ( std::is_same::value ) + return std::make_tuple(static_cast>(raw[0]), std::move(b)); + + if constexpr ( std::is_same::value ) { + auto x = static_cast(raw[0]); // Forced cast to skip safe range check. + return std::make_tuple(static_cast>(x), std::move(b)); + } + + if constexpr ( std::is_same::value || std::is_same::value ) + return detail::unpack(std::move(b), raw, {0, 1}); + + if constexpr ( std::is_same::value || std::is_same::value ) + return detail::unpack(std::move(b), raw, {0, 1, 2, 3}); + + if constexpr ( std::is_same::value || std::is_same::value ) + return detail::unpack(std::move(b), raw, {0, 1, 2, 3, 4, 5, 6, 7}); + + abort_with_backtrace(); + + case ByteOrder::Little: + if constexpr ( std::is_same::value ) + return std::make_tuple(static_cast>(raw[0]), std::move(b)); + + if constexpr ( std::is_same::value ) { + auto x = static_cast(raw[0]); // Forced cast to skip safe range check. + return std::make_tuple(static_cast>(x), std::move(b)); + } + + if constexpr ( std::is_same::value || std::is_same::value ) + return detail::unpack(std::move(b), raw, {1, 0}); + + if constexpr ( std::is_same::value || std::is_same::value ) + return detail::unpack(std::move(b), raw, {3, 2, 1, 0}); + + if constexpr ( std::is_same::value || std::is_same::value ) + return detail::unpack(std::move(b), raw, {7, 6, 5, 4, 3, 2, 1, 0}); + + abort_with_backtrace(); + + case ByteOrder::Host: + // Cannot reach, we check this above. + abort_with_backtrace(); + } + + cannot_be_reached(); +} + +/** + * Converts a 64-bit value from host-order to network order. + * + * v: The value to convert. + */ +extern uint64_t hton64(uint64_t v); + +/** + * Converts a 32-bit value from host-order to network order. + * + * v: The value to convert. + */ +extern uint32_t hton32(uint32_t v); + +/** + * Converts a 16-bit value from host-order to network order. + * + * v: The value to convert. + */ +extern uint16_t hton16(uint16_t v); + +/** + * Converts a 64-bit value from host-order to network order. + * + * v: The value to convert. + */ +extern uint64_t ntoh64(uint64_t v); + +/** + * Converts a 32-bit value from host-order to network order. + * + * v: The value to convert. + */ +extern uint32_t ntoh32(uint32_t v); + +/** + * Converts a 16-bit value from host-order to network order. + * + * v: The value to convert. + */ +extern uint16_t ntoh16(uint16_t v); + +/** + * Revers the bytes of a 16-bit value. + * + * v: The value to convert. + */ +extern uint16_t flip16(uint16_t v); + +/** + * Revers the bytes of a 32-bit value. + * + * v: The value to convert. + */ +extern uint32_t flip32(uint32_t v); + +/** + * Revers the bytes of a 64-bit value. + * + * v: The value to convert. + */ +extern uint64_t flip64(uint64_t v); + +/** + * Flips a signed integer's byte order. + * + * @param v integer to flip + * @param n number of valid bits in *v* + * @return value with *n* bits of *v* flipped in their byte order + */ +inline int64_t flip(int64_t v, uint64_t n) { + auto i = static_cast(v); + i = flip64(i) >> (64 - n * 8); + return static_cast(i); +} + +/** + * Flips an unsigned integer's byte order. + * + * @param v unsigned integer to flip + * @param n number of valid bits in *v* + * @return value with *n* bits of *v* flipped in their byte order + */ +inline uint64_t flip(uint64_t v, uint64_t n) { return (flip64(v) >> (64 - n * 8)); } + +/** Available bit orders. */ +enum class BitOrder { LSB0, MSB0, Undef }; + +/** Extracts a range of bits from an intege value, shifting them to the very left before returning. */ +template +inline hilti::rt::integer::safe bits(hilti::rt::integer::safe v, uint64_t lower, uint64_t upper, + BitOrder bo) { + const auto width = std::numeric_limits::digits; + switch ( bo ) { + case BitOrder::LSB0: break; + + case BitOrder::MSB0: + lower = (width - lower - 1); + upper = (width - upper - 1); + std::swap(lower, upper); + break; + + case BitOrder::Undef: throw RuntimeError("undefined bit order"); + } + + assert(lower <= upper); + auto mask = ((1U << (upper - lower + 1)) - 1U) << lower; + return (v & mask) >> lower; +} + +} // namespace integer +} // namespace hilti::rt diff --git a/hilti/include/rt/types/interval.h b/hilti/include/rt/types/interval.h new file mode 100644 index 000000000..edc929a48 --- /dev/null +++ b/hilti/include/rt/types/interval.h @@ -0,0 +1,105 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +#include +#include + +namespace hilti::rt { + +/** + * Represents HILTI's interval type. Intervals are stored with nanoseconds + * resolution. Intervals can be either positive or negative. + */ +class Interval { +public: + /** + * Constructs a time from a nanoseconds value. + * + * @param nsecs nanonseconds since the UNIX epoch. + */ + explicit Interval(int64_t nsecs = 0) : _nsecs(nsecs) {} + + /** + * Constructs an interval from an unsigned integer value. + * + * @param nsecs interval in nanoseconds. + */ + explicit Interval(hilti::rt::integer::safe nsecs) : _nsecs(nsecs) {} + + /** + * Constructs an interval from an unsigned integer value. + * + * @param nsecs interval in nanoseconds. + */ + explicit Interval(hilti::rt::integer::safe nsecs) : _nsecs(nsecs) {} + + /** + * Constructs an interval from a double value. + * + * @param secs interval in seconds. + */ + explicit Interval(double secs) : _nsecs(static_cast(secs * 1e9)) {} + + Interval(const Interval&) = default; + Interval(Interval&&) noexcept = default; + ~Interval() = default; + + Interval& operator=(const Interval&) = default; + Interval& operator=(Interval&&) noexcept = default; + + /** Returns interval as seconds. */ + double seconds() const { return _nsecs / 1e9; } + + /** Returns interval as nanoseconds. */ + int64_t nanoseconds() const { return _nsecs; } + + bool operator==(const Interval& other) const { return _nsecs == other._nsecs; } + bool operator!=(const Interval& other) const { return _nsecs != other._nsecs; } + bool operator<(const Interval& other) const { return _nsecs < other._nsecs; } + bool operator<=(const Interval& other) const { return _nsecs <= other._nsecs; } + bool operator>(const Interval& other) const { return _nsecs > other._nsecs; } + bool operator>=(const Interval& other) const { return _nsecs >= other._nsecs; } + + Interval operator+(const Interval& other) const { return Interval(_nsecs + other._nsecs); } + Interval operator-(const Interval& other) const { return Interval(_nsecs - other._nsecs); } + + Interval operator*(hilti::rt::integer::safe i) const { + return Interval(static_cast(_nsecs * i)); + } + Interval operator*(hilti::rt::integer::safe i) const { + return Interval(static_cast(_nsecs * i)); + } + + Interval operator*(double i) const { return Interval(static_cast(_nsecs * i)); } + + /** Returns true if the interval is non-zero. */ + operator bool() const { return _nsecs == 0.0; } + + /** Returns a humand-readable representation of the interval. */ + operator std::string() const { + int64_t secs = _nsecs / 1000000000; + // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + double frac = (_nsecs % 1000000000) / 1e9; + return fmt("%.6fs", static_cast(secs) + frac); + } + +private: + int64_t _nsecs = 0; +}; + +namespace detail::adl { +inline std::string to_string(const Interval& x, adl::tag /*unused*/) { return x; } + +} // namespace detail::adl + +inline std::ostream& operator<<(std::ostream& out, const Interval& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/list.h b/hilti/include/rt/types/list.h new file mode 100644 index 000000000..d90ffcb66 --- /dev/null +++ b/hilti/include/rt/types/list.h @@ -0,0 +1,164 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * A list that for large part builds on std::list, but adds a couple of things: + * + * - We add safe HILTIs-side iterators become detectably invalid when the main + * containers gets destroyed. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace hilti::rt { + +template +class List; + +namespace list { + +template +class SafeIterator : public detail::iterator::SafeIterator, typename List::SafeIterator, SafeIterator> { +public: + using Base = detail::iterator::SafeIterator, typename List::SafeIterator, SafeIterator>; + using Base::Base; +}; + +template +class SafeConstIterator + : public detail::iterator::SafeIterator, typename List::ConstIterator, SafeConstIterator> { +public: + using Base = detail::iterator::SafeIterator, typename List::ConstIterator, SafeConstIterator>; + using Base::Base; +}; + +} // namespace list + +/** HILTI's `List` is just strong typedef for `std::list`. */ +template +class List : public std::list, public detail::iterator::Controllee { +public: + using L = std::list; + using C = detail::iterator::Controllee; + + using ConstIterator = typename L::const_iterator; + using SafeIterator = typename L::iterator; + + using typename L::list; +}; + +namespace list { + +// template::type> +template +hilti::rt::List make(const C& input, std::function func) { + hilti::rt::List output; + for ( auto&& i : input ) + output.emplace_back(func(i)); + + return output; +} + +template +hilti::rt::List make(const C& input, std::function func, std::function pred) { + hilti::rt::List output; + for ( auto&& i : input ) + if ( pred(i) ) + output.emplace_back(func(i)); + + return output; +} + +/** Place-holder type for an empty list that doesn't have a known element type. */ +struct Empty {}; + +template +inline bool operator==(const List& v, const Empty& /*unused*/) { + return v.empty(); +} +template +inline bool operator==(const Empty& /*unused*/, const List& v) { + return v.empty(); +} +template +inline bool operator!=(const List& v, const Empty& /*unused*/) { + return ! v.empty(); +} +template +inline bool operator!=(const Empty& /*unused*/, const List& v) { + return ! v.empty(); +} + +inline auto safe_begin(const Empty& x, detail::adl::tag /*unused*/) { return &x; } +inline auto safe_end(const Empty& x, detail::adl::tag /*unused*/) { return &x; } +} // namespace list + +namespace detail::adl { +template +inline std::string to_string(const List& x, adl::tag /*unused*/) { + return fmt("[%s]", rt::join(rt::transform(x, [](const T& y) { return rt::to_string(y); }), ", ")); +} + +inline std::string to_string(const list::Empty& x, adl::tag /*unused*/) { return "[]"; } + +template +inline std::string to_string(const list::SafeIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline std::string to_string(const list::SafeConstIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline auto safe_begin(const List& x, adl::tag /*unused*/) { + return list::SafeConstIterator(x, x.begin()); +} + +template +inline auto safe_end(const List& x, adl::tag /*unused*/) { + return list::SafeConstIterator(x, x.end()); +} + +template +inline auto safe_begin(List& x, adl::tag /*unused*/) { + return list::SafeIterator(x, x.begin()); +} + +template +inline auto safe_end(List& x, adl::tag /*unused*/) { + return list::SafeIterator(x, x.end()); +} + +} // namespace detail::adl + +template +inline std::ostream& operator<<(std::ostream& out, const List& x) { + out << to_string(x); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const list::Empty& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const list::SafeIterator& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const list::SafeConstIterator& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/map.h b/hilti/include/rt/types/map.h new file mode 100644 index 000000000..912af95a9 --- /dev/null +++ b/hilti/include/rt/types/map.h @@ -0,0 +1,227 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * A map that mostly builds on std::map, but adds a couple of things: + * + * - We add safe HILTI-side iterators become detectably invalid when the main + * containers gets destroyed. + * + * - [Future] Automatic element expiration. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace hilti::rt { + +template +class Map; + +namespace map { + +template +class SafeIterator : public hilti::rt::detail::iterator::SafeIterator, typename Map::SafeIterator, + SafeIterator> { +public: + using Base = + hilti::rt::detail::iterator::SafeIterator, typename Map::SafeIterator, SafeIterator>; + using Base::Base; +}; + +template +class SafeConstIterator + : public hilti::rt::detail::iterator::SafeIterator, typename Map::ConstIterator, + SafeConstIterator> { +public: + using Base = hilti::rt::detail::iterator::SafeIterator, typename Map::ConstIterator, + SafeConstIterator>; + using Base::Base; +}; + +} // namespace map + +// Proxy to faciliate safe assignment. + +/** HILTI's `Map` is an extended version `std::map`. */ +template +class Map : public std::map, public hilti::rt::detail::iterator::Controllee { +public: + using M = std::map; + using C = hilti::rt::detail::iterator::Controllee; + + using ConstIterator = typename M::const_iterator; + using SafeIterator = typename M::iterator; + + /** Returns true if a specific key is part of the set. */ + bool contains(const K& k) { return this->find(k) != this->end(); } + + /** + * Returns the value for a given key, with an optional default if not foound. + * + * @param k key to retrieve + * @param default_ if given, a defautl to return if *k* is not part of the map. + * + * @throws `IndexError` if `k` is not part of the map and no default has + * been given. + */ + const V& get(const K& k, std::optional default_ = {}) const { + if ( const auto& i = this->find(k); i != this->end() ) + return i->second; + + if ( default_ ) + return *default_; + + throw IndexError("key does not exist"); + } + + const V& operator[](const K& k) const { return get(k); } + auto operator[](const K& k); +}; + +namespace map::detail { + +template +class AssignProxy { +public: + using M = std::map; + + AssignProxy(M* m, K k) : _m(m), _k(std::move(k)) {} + + const V& get() const { + if ( _m->find(_k) != _m->end() ) + return (*_m)[_k]; + + throw IndexError(fmt("map key does not exist")); + } + + AssignProxy& operator=(V v) { + (*_m)[_k] = std::move(v); + return *this; + } + + operator V() const { return get(); } + + bool operator==(const V& v) { return get() == v; } + bool operator!=(const V& v) { return get() != v; } + +private: + M* _m; + K _k; +}; + +} // namespace map::detail + +template +inline auto Map::operator[](const K& k) { + return hilti::rt::map::detail::template AssignProxy(this, k); +} + +template +inline std::ostream& operator<<(std::ostream& out, const hilti::rt::map::detail::AssignProxy& x) { + out << x.get(); + return out; +} + +namespace map { +/** Place-holder type for an empty map that doesn't have a known element type. */ +class Empty : public Map {}; + +template +inline bool operator==(const Map& v, const Empty& /*unused*/) { + return v.empty(); +} +template +inline bool operator==(const Empty& /*unused*/, const Map& v) { + return v.empty(); +} +template +inline bool operator!=(const Map& v, const Empty& /*unused*/) { + return ! v.empty(); +} +template +inline bool operator!=(const Empty& /*unused*/, const Map& v) { + return ! v.empty(); +} +} // namespace map + +namespace detail::adl { +template +inline std::string to_string(const Map& x, adl::tag /*unused*/) { + std::vector r; + + for ( const auto& i : x ) + r.push_back(fmt("%s: %s", i.first, i.second)); + + return fmt("{%s}", rt::join(r, ", ")); +} + +template +inline std::string to_string(const map::detail::AssignProxy& x, adl::tag /*unused*/) { + return hilti::rt::to_string(x.get()); +} + +inline std::string to_string(const map::Empty& x, adl::tag /*unused*/) { return "{}"; } + +template +inline std::string to_string(const map::SafeIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline std::string to_string(const map::SafeConstIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline auto safe_begin(const Map& x, adl::tag /*unused*/) { + return map::SafeConstIterator(x, x.begin()); +} + +template +inline auto safe_begin(Map& x, adl::tag /*unused*/) { + return map::SafeIterator(x, x.begin()); +} + +template +inline auto safe_end(const Map& x, adl::tag /*unused*/) { + return map::SafeConstIterator(x, x.end()); +} + +template +inline auto safe_end(Map& x, adl::tag /*unused*/) { + return map::SafeIterator(x, x.end()); +} + +} // namespace detail::adl + +template +inline std::ostream& operator<<(std::ostream& out, const Map& x) { + out << to_string(x); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const map::Empty& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const map::SafeIterator& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const map::SafeConstIterator& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/network.h b/hilti/include/rt/types/network.h new file mode 100644 index 000000000..7fb58a440 --- /dev/null +++ b/hilti/include/rt/types/network.h @@ -0,0 +1,92 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace hilti::rt { + +/** + * Represents HILTI's network type. + */ +class Network { +public: + /** + * Constructs a network from prefix address and length. + * + * @param prefix address, which's *length* lower bits will be masked out. + * @param prefix length, which must be in the range from 0-32 for IPv4 + * addresses; and 0-128 for IPv6 addresses. + */ + Network(const Address& prefix, int length) : _prefix(prefix), _length(length) { _mask(); } + + /** Constructs a network from prefix address and length. + * + * @param prefix string representation of an address, which's *length* + * lower bits will be masked out. + * @param prefix length, which must be in the range from 0-32 for IPv4 + * addresses; and 0-128 for IPv6 addresses. + * + * @throws RuntimeError if it cannot parse the prefix into a valid IPv4 or IPv6 address. + */ + Network(const std::string& prefix, int length) : _prefix(prefix), _length(length) { _mask(); } + Network(const Network&) = default; + Network() = default; + Network(Network&&) noexcept = default; + ~Network() = default; + + Network& operator=(const Network&) = default; + Network& operator=(Network&&) noexcept = default; + + /** Returns the network prefix, with the lower bitsmasked out. */ + const auto& prefix() const { return _prefix; } + + /** Returns the protocol family of the networ, which can be IPv4 or IPv6. */ + auto family() const { return _prefix.family(); } + + /** + * Returns the length of the prefix. If the prefix' protocol family is + * IPv4, this will be between 0 and 32; if IPv6, between 0 and 128. + */ + auto length() const { return (family() == AddressFamily::IPv4 ? _length - 96 : _length); } + + /** Returns true if the network includes a given address. */ + bool contains(const Address& x) const { return x.mask(_length) == _prefix; } + + bool operator==(const Network& other) const { return _prefix == other._prefix && _length == other._length; } + bool operator!=(const Network& other) const { return ! (*this == other); } + + /** + * Returns a humand-readable represenation of the network, using the same + * format that the corresponding constructor parses. + */ + operator std::string() const { return fmt("%s/%u", _prefix, length()); } + +private: + void _mask() { + if ( _prefix.family() == AddressFamily::IPv4 ) + _length += 96; + + _prefix = _prefix.mask(_length); + } + + Address _prefix; + int _length = 0; +}; + +namespace detail::adl { +inline std::string to_string(const Network& x, adl::tag /*unused*/) { return x; } +} // namespace detail::adl + +inline std::ostream& operator<<(std::ostream& out, const Network& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/null.h b/hilti/include/rt/types/null.h new file mode 100644 index 000000000..1efce4115 --- /dev/null +++ b/hilti/include/rt/types/null.h @@ -0,0 +1,27 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +#include + +namespace hilti::rt { + +/** + * Represents HILTI's "null" type. + */ +struct Null {}; + +namespace detail::adl { +inline std::string to_string(const Null& x, adl::tag /*unused*/) { return "Null"; } +} // namespace detail::adl + +inline std::ostream& operator<<(std::ostream& out, const Null& x) { + out << "Null"; + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/optional.h b/hilti/include/rt/types/optional.h new file mode 100644 index 000000000..b07bd3c8e --- /dev/null +++ b/hilti/include/rt/types/optional.h @@ -0,0 +1,80 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti::rt { + +namespace detail::adl { +template +inline std::string to_string(std::optional x, adl::tag /*unused*/) { + return x ? hilti::rt::to_string(*x) : "(not set)"; +} + +} // namespace detail::adl + +/** + * Exception reflecting an access to an unset optional value. + */ +HILTI_EXCEPTION(UnsetOptional, RuntimeError) + +namespace optional { + +struct Unset : public std::exception {}; // Internal exception to signal access to optional that may expectedly by unset + +template +inline auto& value(const std::optional& t, const char* location) { + if ( t.has_value() ) + return t.value(); + + throw UnsetOptional("unset optional value", location); +} + +template +inline auto& value(std::optional& t, const char* location) { + if ( t.has_value() ) + return t.value(); + + throw UnsetOptional("unset optional value", location); +} + +template +inline auto& valueOrInit(std::optional& t, const T& default_) { + if ( ! t.has_value() ) + t = default_; + + return t.value(); +} + +template +inline auto& valueOrInit(std::optional& t) { + if ( ! t.has_value() ) + t.emplace(); + + return t.value(); +} + +template +inline auto& tryValue(const std::optional& t) { + if ( t.has_value() ) + return t.value(); + + throw Unset(); +} + +} // namespace optional + +template<> +inline std::string detail::to_string_for_print>(const std::optional& x) { + return x ? *x : "(not set)"; +} + +template<> +inline std::string detail::to_string_for_print>( + const std::optional& x) { + return x ? std::string(*x) : "(not set)"; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/port.h b/hilti/include/rt/types/port.h new file mode 100644 index 000000000..e81cbfb9e --- /dev/null +++ b/hilti/include/rt/types/port.h @@ -0,0 +1,86 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace hilti::rt { + +/** Protocols that can be asociated with a `Port`. */ +enum class Protocol { Undef = 0, TCP, UDP, ICMP }; + +/** + * Represents HILTI's port type. A port is pair of port number and protocol. + */ +class Port { +public: + /** + * Constructs a port value. from port number and protocol. + */ + Port(uint16_t port, Protocol protocol) : _port(port), _protocol(protocol) {} + + /** + * Constructs a port from a textual representation of the form `//`. + * + * @throws RuntimeError if it cannot parse the port specification + * (whereby, however, an unsupported protocol doesn't count as an error; + * it'll be left as `Undef`) + */ + explicit Port(const std::string& port) { _parse(port); } + + Port() = default; + Port(const Port&) = default; + Port(Port&&) noexcept = default; + ~Port() = default; + + Port& operator=(const Port&) = default; + Port& operator=(Port&&) noexcept = default; + + /** Returns the port's number. */ + auto port() const { return _port; } + + /** Returns the port's protocol. */ + auto protocol() const { return _protocol; } + + bool operator==(const Port& other) const { return _port == other._port && _protocol == other._protocol; } + bool operator!=(const Port& other) const { return ! (*this == other); } + + /** + * Returns a human-readable representation of the port, using the same + * format that the corresponding constructor parses. + */ + operator std::string() const; + +private: + // Throws RuntimeError if it cannot parse the address. + void _parse(const std::string& port); + + uint16_t _port = 0; + Protocol _protocol = Protocol::Undef; +}; + +namespace detail::adl { +extern std::string to_string(const Protocol& x, adl::tag /*unused*/); +inline std::string to_string(const Port& x, adl::tag /*unused*/) { return x; }; +} // namespace detail::adl + +inline std::ostream& operator<<(std::ostream& out, const Protocol& x) { + out << to_string(x); + return out; +} +inline std::ostream& operator<<(std::ostream& out, const Port& x) { + out << to_string(x); + return out; +} + + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/real.h b/hilti/include/rt/types/real.h new file mode 100644 index 000000000..2e3d0d8d9 --- /dev/null +++ b/hilti/include/rt/types/real.h @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace hilti::rt { + +namespace real { +/** Available formats for unpacking a binary floating point value. */ +enum class Type { Undef, IEEE754_Single, IEEE754_Double }; + +/** Unpacks a floatingpoint value from a binary represenation, following the protocol for `unpack` operator. */ +extern Result> unpack(const Bytes& data, Type type, ByteOrder fmt); + +/** Unpacks a floatingpoint value from a binary represenation, following the protocol for `unpack` operator. */ +extern Result> unpack(const stream::View& data, Type type, ByteOrder fmt); + +} // namespace real + +namespace detail::adl { +inline std::string to_string(double x, adl::tag /*unused*/) { + // %g general floating point format drops '.' + return fmt("%g", x); +} + +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/reference.h b/hilti/include/rt/types/reference.h new file mode 100644 index 000000000..069968563 --- /dev/null +++ b/hilti/include/rt/types/reference.h @@ -0,0 +1,758 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace hilti::rt { + +namespace trait { +struct isStruct; +} // namespace trait + +/** Exception indicating access to an unset (null) reference. **/ +HILTI_EXCEPTION(NullReference, RuntimeError) + +/** Exception indicating access to an already expired weak reference. **/ +HILTI_EXCEPTION(ExpiredReference, RuntimeError) + +/** Exception indicating an undefined use of a reference type. */ +HILTI_EXCEPTION(IllegalReference, RuntimeError) + +/** Base for classes that `ValueReference::self` can receive. */ +template +using Controllable = std::enable_shared_from_this; + +/** + * Class representing HILTI's `value_ref` type. This class stores an value + * of type T on the heap and imposes value semantics on it. In particular, + * copying a `ValueReference` will link the new instance to its own copy of + * the managed value. + * + * Generally, a value reference will always have a value associated with it. + * There are however ways to create it without one. Accesses that require a + * value, are checked and will abort in that case. + * + * Other reference types (`StrongReference`, `WeakReference`) can bind to an + * existing value reference, essentially creating handles to its value. They + * then become joined managers of the value. + * + * @note It seems we could clean up this class and get rid of the internal + * variant altogether. We need the variant only to potentially store a raw + * pointer coming in through the corresponding constructor. However, we + * require that pointer to point to a `Controllable` and hence could turn it + * into a shared_ptr right there. On the downside, that would mean a + * `shared_from_this()` call even if the resulting instance is never used -- + * which with the current code generator could happen frequently (at least + * once we optimizde to use `this` instead of the `self` wrapper when + * possible). So leaving it alone for now. + */ +template +class ValueReference { +public: + /** + * Instantiates a reference containing a new value of `T` initialized to + * its default value. + */ + ValueReference() : _ptr(std::make_shared()) {} + + /** + * Instantiates a reference containing a new value of `T` initialized to + * a given value. + * + * @param t value to initialize new instance with + */ + ValueReference(T t) : _ptr(std::make_shared(std::move(t))) {} + + /** + * Instantiates a new reference from an existing `std::shared_ptr` to a + * value of type `T`. This does *not* copy the pointer's target value; + * the new reference will keep a pointer to the same value. + * + * This constructor is mostly for internal purposes to create a new value + * reference that's associatged with an existing `StrongReference`. + * + * @param t shared pointer to link to + */ + explicit ValueReference(std::shared_ptr t) : _ptr(std::move(t)) {} + + /** + * Copy constructor. The new instance will refer to a copy of the + * source's value. + */ + ValueReference(const ValueReference& other) { + if ( auto ptr = other._get() ) + _ptr = std::make_shared(*ptr); + else + _ptr = std::shared_ptr(); + } + + /** Move constructor. */ + ValueReference(ValueReference&& other) noexcept = default; + + /** Destructor. */ + ~ValueReference() {} + + /** + * Returns true if the reference does not contain a value. This will + * rarely happen, except when explicitly constructed that way through an + * existing pointer. + */ + bool isNull() const { + assert(_ptr.index() != std::variant_npos); + return _get() == nullptr; + } + + /** + * Returns a pointer to the referred value. The result may be null if the + * instance does not refer to a valid value. + */ + const T* get() const { return _get(); } + + /** + * Returns a shared pointer to the referred value. The result may be a + * null pointer if the instance does not refer to a valid value. + * + * For this to work, the value reference must have either (1) create the + * contained value itself through one of the standard constructor; or (2) + * if created through an explicit pointer constructor, the instance must + * be located on the heap and be the instance of a classed derived from + * `Controllable`. + * + * @throws IllegalReference if no shared pointer can be constructed for + * the contained instance. + */ + std::shared_ptr asSharedPtr() const { + assert(_ptr.index() != std::variant_npos); + + if ( auto x = std::get_if>(&_ptr) ) + return *x; + + try { + if ( auto ptr = std::get(_ptr) ) { + if constexpr ( std::is_base_of, T>::value ) + return ptr->shared_from_this(); + else + throw IllegalReference("cannot dynamically create reference for type"); + } + else + throw IllegalReference("unexpected state of value reference"); + } catch ( const std::bad_weak_ptr& ) { + throw IllegalReference("reference to non-heap instance"); + } + } + + /** + * Resets the contained value to a fresh copy of a `T` value initialized + * to its default. + */ + void reset() { _ptr = std::shared_ptr(); } + + /** + * Returns a reference to the contained value. + * + * @throws NullReference if the instance does not refer to a valid value + */ + const T& operator*() const { return *_safeGet(); } + + /** + * Returns a reference to the contained value. + * + * @throws NullReference if the instance does not refer to a valid value + */ + T& operator*() { return *_safeGet(); } + + /** + * Returns a pointer to the contained value. + * + * @throws NullReference if the instance does not refer to a valid value + */ + const T* operator->() const { return _safeGet(); } + + /** + * Returns a pointer to the contained value. + * + * @throws NullReference if the instance does not refer to a valid value + */ + T* operator->() { return _safeGet(); } + + /** + * Compares the values of two references. + * + * @throws NullReference if one of the instances does not refer to a + * valid value + */ + bool operator==(const ValueReference& other) const { return *_safeGet() == *other._safeGet(); } + + /** + * Compares the values of two references. + * + * @throws NullReference if one of the instances does not refer to a + * valid value + */ + bool operator==(const T& other) const { return *_safeGet() == other; } + + /** + * Compares the values of two references. + * + * @throws NullReference if one of the instances does not refer to a + * valid value + */ + bool operator!=(const ValueReference& other) const { return *_safeGet() != *other._safeGet(); } + + /** + * Compares the values of two references. + * + * @throws NullReference if one of the instances does not refer to a + * valid value + */ + bool operator!=(const T& other) const { return *_safeGet() != other; } + + /** + * Assigns to the contained value. Assgining does not invalidate other + * references associated with the same value; they'll see the change. + * + * @throws NullReference if the instance does not currently refer to a valid value + */ + ValueReference& operator=(T other) { + *_safeGet() = std::move(other); + return *this; + } + + /** + * Assigns to the contained value. Assgining does not invalidate other + * references associated with the same value; they'll see the change. + * + * @throws NullReference if the instance does not currently refer to a valid value + */ + ValueReference& operator=(const ValueReference& other) { + if ( &other != this ) + *_safeGet() = *other._safeGet(); + + return *this; + } + + /** + * Assigns to the contained value. Assgining does not invalidate other + * references associated with the same value; they'll see the change. + * + * @throws NullReference if the instance does not currently refer to a valid value + */ + ValueReference& operator=(ValueReference&& other) noexcept { + if ( &other != this ) { + *_safeGet() = std::move(*other._safeGet()); + other._ptr = nullptr; + } + + return *this; + } + + /** + * Shortcut to create a new instance refering to an existing value of + * type `T`. `T` must be derived from `Controllable`. + * + * This is for internal use by the code generator to wrap `this` inside + * methods into a value reference. + */ + static ValueReference self(T* t) { + static_assert(std::is_base_of, T>::value); + return ValueReference(t); + } + +private: + /** + * Instantiates a reference from an existing raw pointer to a value of + * type 'T`, which must be derived from `Controllable`. + * + * This does *not* copy the value being pointed to; the new + * reference will keep a pointer to the same value. That also means it's + * not safe to delete the pointed-to instance while the value reference + * stays around. + */ + explicit ValueReference(T* t) : _ptr(t) { static_assert(std::is_base_of, T>::value); } + + const T* _get() const { + if ( auto ptr = std::get_if(&_ptr) ) + return *ptr; + + if ( auto ptr = std::get_if>(&_ptr) ) + return (*ptr).get(); + + throw IllegalReference("unexpted variant type in val_ref"); + } + + T* _get() { + if ( auto ptr = std::get_if(&_ptr) ) + return *ptr; + + if ( auto ptr = std::get_if>(&_ptr) ) + return (*ptr).get(); + + throw IllegalReference("unexpted variant type in val_ref"); + } + + const T* _safeGet() const { + if ( _ptr.valueless_by_exception() ) + throw NullReference("no valid value"); + + if ( _ptr.index() == std::variant_npos ) + throw NullReference("no valid value"); + + if ( auto ptr = _get() ) + return ptr; + + throw NullReference("attempt to access null value reference"); + } + + T* _safeGet() { + if ( _ptr.valueless_by_exception() ) + throw NullReference("no valid value"); + + if ( _ptr.index() == std::variant_npos ) + throw NullReference("no valid value"); + + if ( auto ptr = _get() ) + return ptr; + + throw NullReference("attempt to access null reference"); + } + + std::variant, T*> _ptr; +}; + +/** + * A strong reference to a shared value. This is essentially a `shared_ptr` + * that can bind to the values of `ValueReference` or `WeakReferecne.` + * + * Note that different from `ValueReference`, a strong reference can + * explicitly be null. + */ +template +class StrongReference : public std::shared_ptr { +public: + using Base = std::shared_ptr; + + /** Default constructor creating a null reference. */ + StrongReference() : Base() {} + + /** + * Instantiates a reference pointing to a newly allocated value. + * + * @param t initialization value + */ + explicit StrongReference(T t) : Base(std::make_shared(std::move(t))) {} + + /** + * Instantiates a reference pointing to the value refered to be an + * existing `ValueReference`. This does not copy the value, it will be + * shared (and managed jointly) afterwards. + */ + explicit StrongReference(const ValueReference& t) : Base(t.asSharedPtr()) {} + + /** + * Copy constructor. This copies the reference, not the value, which will + * be shared afterwards. + */ + StrongReference(const StrongReference& other) : Base(other) {} + + /** Move constructor. */ + StrongReference(StrongReference&& other) noexcept : Base(std::move(other)) {} + + /** Destructor. */ + ~StrongReference() {} + + /** + * Returns true if the reference does not refer any value. + */ + bool isNull() const { return this->get() == nullptr; } + + /** + * Returns a value reference that is linked to the refered value. If the + * strong reference is null, the returns referecne will be so, too. + */ + ValueReference derefAsValue() const { return ValueReference(*this); } + + /** + * Resets the reference to a null value, releasing any ownership it still + * holds. + */ + void reset() { Base::operator=(nullptr); } + + /** + * Returns the contained value. + * + * @throws NullReference if the instance is null. + */ + const T& operator*() const { + _check(); + return *this->get(); // NOLINT + } + + /** + * Returns the contained value. + * + * @throws NullReference if the instance is null. + */ + T& operator*() { + _check(); + return *this->get(); // NOLINT + } + + /** + * Returns a pointer to the contained value. + * + * @throws NullReference if the instance is null. + */ + const T* operator->() const { + _check(); + return this->get(); + } + + /** + * Returns a pointer to the contained value. + * + * @throws NullReference if the instance is null. + */ + T* operator->() { + _check(); + return this->get(); + } + + /** Returns true if the reference is not null. */ + operator bool() const { return ! isNull(); } + + /** + * Reinitializes the reference with a newly allocated value, releasing + * any previous ownership still held. + * + * @param t value to allocate and then refer to + */ + StrongReference& operator=(T other) { + Base::operator=(std::make_shared(std::move(other))); + return *this; + } + + /** + * Reinitiqalized the reference to now point to to the value refered to + * be an existing `ValueReference`. This does not copy that value, it + * will be shared (and managed jointly) afterwards. + */ + StrongReference& operator=(const ValueReference& other) { + Base::operator=(other.asSharedPtr()); + return *this; + } + + /** Copy assignment. This will share ownership, not copy the value. */ + StrongReference& operator=(const StrongReference& other) { + Base::operator=(other); + return *this; + } + + /** Move assignment. */ + StrongReference& operator=(StrongReference&& other) noexcept { + Base::operator=(std::move(other)); + return *this; + } + +private: + void _check() const { + if ( ! *this ) + throw NullReference("attempt to access null reference"); + } +}; + +/** + * A weak reference to a shared value. This is essentially a `weak_ptr` that + * can bind to the values of `ValueReference` or `StrongReference.` The weak + * reference will remain valid until all linked strong/value references have + * ceased to exist. + * + * Note that different from `ValueReference`, a weak reference can explicitly + * be null. + */ +template +class WeakReference : public std::weak_ptr { +public: + using Base = std::weak_ptr; + + /** Default constructor creating a null reference. */ + WeakReference() : Base() {} + + /** + * Instantiates a reference pointing to the value refered to be an + * existing `ValueReference`. This does not copy the value, it will be + * shared afterwards. + */ + explicit WeakReference(const ValueReference& t) : Base(t.asSharedPtr()) {} + + /** + * Instantiates a reference pointing to the value refered to be an + * existing `StrongReference`. This does not copy the value, it will be + * shared afterwards. + */ + explicit WeakReference(const StrongReference& t) : Base(t) {} + + /** + * Copy constructor. This copies the reference, not the value, which will + * be shared afterwards. + */ + WeakReference(const WeakReference& other) : Base(other) {} + + /** Move constructor. */ + WeakReference(WeakReference&& other) noexcept : Base(std::move(other)) {} + + /** Destructor. */ + ~WeakReference() {} + + /** Returns true if the reference is either null or expired. */ + bool isNull() const { return this->lock() == nullptr; } + + /** + * Returns true if the reference was pointing to a non-null value that + * has now expired. + */ + bool isExpired() const { + auto is_default = ! this->owner_before(Base{}) && ! Base{}.owner_before(*this); + return this->expired() && ! is_default; + } + + /** + * Returns a pointer to the value being refered to. This will be null if + * the weak point is null or expired. + */ + const T* get() const { return this->lock().get(); } + + /** + * Returns a value reference that is linked to the refered value. If the + * weak reference is null or expired, the returned reference will be null. + */ + ValueReference derefAsValue() const { return ValueReference(this->lock()); } + + /** Resets the reference to a null value. */ + void reset() { Base::reset(); } + + /** + * Returns the contained value. + * + * @throws NullReference or ExpiredReference if the instance is null or + * expired, respectively. + */ + const T& operator*() const { + _check(); + return *this->lock(); + } + + /** + * Returns the contained value. + * + * @throws NullReference or ExpiredReference if the instance is null or + * expired, respectively. + */ + T& operator*() { + _check(); + return *this->lock(); + } + + /** + * Returns a pointer to the contained value. + * + * @throws NullReference or ExpiredReference if the instance is null or + * expired, respectively. + */ + const T* operator->() const { + _check(); + return this->lock().get(); + } + + /** + * Returns a pointer to the contained value. + * + * @throws NullReference or ExpiredReference if the instance is null or + * expired, respectively. + */ + T* operator->() { + _check(); + return this->lock().get(); + } + + /** Returns true if the reference is not null or expired. */ + operator bool() const { return ! isNull(); } + + /** + * Reinitiqalized the reference to now point to to the value refered to + * be an existing `ValueReference`. This does not copy that value, it + * will be shared afterwards. + */ + WeakReference& operator=(const ValueReference& other) { + Base::operator=(other.asSharedPtr()); + return *this; + } + + /** + * Reinitiqalized the reference to now point to to the value refered to + * be an existing `StrongReference`. This does not copy that value, it + * will be shared afterwards. + */ + WeakReference& operator=(const StrongReference& other) { + Base::operator=(other); + return *this; + } + + /** Copy assignment. This will share ownership, not copy the value. */ + WeakReference& operator=(const WeakReference& other) { + Base::operator=(other); + return *this; + } + + /** Move assignment. */ + WeakReference& operator=(WeakReference&& other) noexcept { + Base::operator=(std::move(other)); + return *this; + } + +private: + void _check() const { + if ( isExpired() ) + throw ExpiredReference("attempt to access expired reference"); + + if ( isNull() ) + throw NullReference("attempt to access null reference"); + } +}; + +/** + * Type for a generic, non-templated strong reference binding to a StrongReference. + * This generic version can keep a StrongReference alive, but does not provide + * access to the instance itself. + */ +class StrongReferenceGeneric { +public: + template + StrongReferenceGeneric(StrongReference x) : _ptr(std::move(x)) {} + + template + T* as() const { + return std::any_cast>(&_ptr)->get(); + } + +private: + std::any _ptr; +}; + +namespace reference { + +/** + * Helper to instantiate a strong reference pointing to a newly allocated, + * preinitialized value. + */ +template +StrongReference make_strong(Args&&... args) { + return StrongReference(T(std::forward(args)...)); +} + +/** + * Helper to instantiate a value reference pointing to a newly allocated, + * preinitialized value. + */ +template +ValueReference make_value(Args&&... args) { + return ValueReference(T(std::forward(args)...)); +} + +} // namespace reference + +namespace detail::adl { + +template +inline std::string to_string(const StrongReference& x, adl::tag /*unused*/) { + return x ? hilti::rt::to_string(*x) : "Null"; +} + +template +inline std::string to_string(const WeakReference& x, adl::tag /*unused*/) { + if ( x.isExpired() ) + return ""; + + if ( x.isNull() ) + return "Null"; + + return hilti::rt::to_string(*x); +} + +template +inline std::string to_string(const ValueReference& x, adl::tag /*unused*/) { + return hilti::rt::to_string(*x); +} + +} // namespace detail::adl + +// String specialization + +template<> +inline std::string detail::to_string_for_print>(const StrongReference& x) { + return x ? hilti::rt::to_string_for_print(*x) : "Null"; +} + +template<> +inline std::string detail::to_string_for_print>( + const WeakReference& x) { + if ( x.isExpired() ) + return ""; + + if ( x.isNull() ) + return "Null"; + + return hilti::rt::to_string_for_print(*x); +} + +template<> +inline std::string detail::to_string_for_print>(const ValueReference& x) { + return hilti::rt::to_string_for_print(*x); +} + +// Bytes specialization + +template<> +inline std::string detail::to_string_for_print>(const StrongReference& x) { + return x ? escapeBytes((*x).str(), false, false) : "Null"; +} + +template<> +inline std::string detail::to_string_for_print>(const WeakReference& x) { + if ( x.isExpired() ) + return ""; + + if ( x.isNull() ) + return "Null"; + + return escapeBytes((*x).str(), false, false); +} + +template<> +inline std::string detail::to_string_for_print>(const ValueReference& x) { + return escapeBytes((*x).str(), false, false); +} + +template +inline std::ostream& operator<<(std::ostream& out, const StrongReference& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const ValueReference& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const WeakReference& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/regexp.h b/hilti/include/rt/types/regexp.h new file mode 100644 index 000000000..e04c04533 --- /dev/null +++ b/hilti/include/rt/types/regexp.h @@ -0,0 +1,211 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +extern "C" { +struct jrx_regex_t; +struct jrx_match_state; +} + +namespace hilti::rt { + +class RegExp; + +namespace regexp { + +/** Exception indicating trouble when compiling a regular expression. */ +HILTI_EXCEPTION(PatternError, RuntimeError) + +/** Exception indicating use of unsupport matching capabilities. */ +HILTI_EXCEPTION(NotSupported, RuntimeError) + +/** Exception indicating illegal reuse of MatchState. **/ +HILTI_EXCEPTION(MatchStateReuse, RuntimeError) + +struct Flags { + bool no_sub : 1; /**< Compile without support for capturing sub-expressions. */ +}; + +/** + * Match state for incremental regexp matching. This is tailored for token + * matching: it's anchored and does not support capture groups. + * + * @note We don't make this part of the public API to avoid dependending on + * the jrx header. + **/ +class MatchState { +public: + /** + * Creates a fresh instances ready to match data against a given regular expression. + */ + MatchState(const RegExp& re); + MatchState() noexcept; + ~MatchState(); + MatchState(const MatchState& other); + MatchState(MatchState&& /*unused*/) noexcept; + MatchState& operator=(const MatchState& other); + MatchState& operator=(MatchState&& /*unused*/) noexcept; + + /** + * Feeds the next chunk of data into the matcher. + * + * @param data chunk of data; if the underlying stream is frozen, this + * will be assumd to be the last chunk of data, and + * the result of any further calls to `advance()` will then trigger a + * `MatchStateReuse` exception + * + * @returns A tuple in which the integer is: (1) larger than zero if a + * match has been found; for sets compiled via `compileSet` the integer + * value then indicates the ID of the pattern that was found. (2) zero if + * no match was found and advancing further to more data is guaranteed to + * not change that fact. (3) smaller than 0 if no match was found so far + * but advancing further may change that. In either case, the returned + * view trims *data* to the part not consumed yet. + */ + std::tuple advance(const stream::View& data); + + /** + * Feeds the next chunk of data into the matcher. + * + * @param data chunk of data + * + * @param is_final true to signal the last chunk of data; the result of + * any further calls to `advance()` will then trigger a `MatchStateReuse` + * exception + + * @returns A tuple in which the integer is: (1) larger than zero if a + * match has been found; for sets compiled via `compileSet` the integer + * value then indicates the ID of the pattern that was found. (2) zero if + * no match was found and advancing further to more data is guaranteed to + * not change that fact. (3) smaller than 0 if no match was found so far + * but advancing further may change that. In either case, the 2nd element + * in the tuple returns the number of bytes that were consumed from + * *data* by the matching. + */ + std::tuple advance(const Bytes& data, bool is_final = false); + +private: + std::pair _advance(const stream::View& data, bool is_final); + + // TODO(robin): PIMPLing here means we have to alllocate dynamic memory, which + // isn't great for this class. However, without PIMPL we get a new dependency on + // 'jrx.h', which isn't great either. Better ideas? + struct Pimpl; + std::unique_ptr _pimpl; +}; + +} // namespace regexp + +/** A regular expression instance. */ +class RegExp { +public: + /** + * Instantiates a new regular expression instance. + * + * @param pattern regular expression to compile + * @param flags compilation flags for the regexp + * @exception `PatternError` if the pattern cannot be compiled + */ + RegExp(std::string pattern, regexp::Flags flags); + + /** + * Instantiates a new regular expression instance performing parallel set + * matching on multiple patterns. Set matching implicitly sets the + * `Flags::no_sub` (even if just one pattern is passed in). + * + * @param patterns regular expressions to compile jointly + * @param flags compilation flags for the regexp + * @exception `PatternError` if a pattern cannot be compiled + */ + RegExp(const std::vector& patterns, regexp::Flags flags); + + RegExp() = default; + + const auto& patterns() const { return _patterns; } + const auto& flags() const { return _flags; } + + /** + * Searches a pattern within a bytes view. + * + * @return If the returned integer is larger than zero, the regexp was + * found; for sets compiled via `compileSet` the integer value then + * indicates the ID of the pattern that was found. If the function + * returns zero, no match was found and that wwon't change if further + * data gets added to the input data. If the returned value is smaller than + * 0, a partial match was found (i.e., no match yet but adding further + * data could change that). + */ + int32_t find(const Bytes& data) const; + + /** + * Searches a pattern within a bytes view and returns the matching part. + * + * @return A tuple where the 1st element corresponds to the result of + * `find()`. If that's larger than zero, the 2nd is the matching data. + */ + std::tuple findSpan(const Bytes& data) const; + + /** + * Searches a pattern within a bytes view and returns the matching data + * for all matching capture groups. + * + * @return A vector of containing the matching data for all capture + * groups. The vector's index 0 corresponds to the whole expression, + * index 1 to the first capture group etc. If no match is found, the + * returned vector is empty. + * + * @todo This function does not yet support sets compiled via + * `compileSet()`. + */ + Vector findGroups(const Bytes& data) const; + + /** + * Returns matching state initializes for incremental token matching. For + * token matching the regular expression will be considered implicitly + * anchored. The regular expression must have been compiled with the + * `&nosub` attribute. + */ + regexp::MatchState tokenMatcher() const; + +private: + friend class regexp::MatchState; + + jrx_regex_t* _jrx() const { + assert(_jrx_shared); + return _jrx_shared.get(); + } + const auto& _jrxShared() const { return _jrx_shared; } + + /** + * Searches for the regexp anywhere inside a bytes instance and returns + * the first match. + */ + int16_t _search_pattern(jrx_match_state* ms, const Bytes& data, int32_t* so, int32_t* eo, bool do_anchor, + bool find_partial_matches) const; + + void _newJrx(); + void _compileOne(std::string pattern, int idx); + + regexp::Flags _flags{}; + std::vector _patterns; + std::shared_ptr + _jrx_shared; // Shared ptr so that we can copy by value, and safely share with match state. +}; + +namespace detail::adl { +extern std::string to_string(const RegExp& x, adl::tag /*unused*/); + +inline std::string to_string(const regexp::MatchState& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/result.h b/hilti/include/rt/types/result.h new file mode 100644 index 000000000..02f2478c2 --- /dev/null +++ b/hilti/include/rt/types/result.h @@ -0,0 +1,34 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti::rt { + +namespace detail::adl { +template +inline std::string to_string(Result x, adl::tag /*unused*/) { + return x ? hilti::rt::to_string(*x) : hilti::rt::to_string(x.error()); +} + +template +inline std::string to_string_for_print(Result x, adl::tag /*unused*/) { + return x ? hilti::rt::to_string_for_print(*x) : hilti::rt::to_string(x.error()); +} + +} // namespace detail::adl + +template<> +inline std::string detail::to_string_for_print>(const Result& x) { + return x ? *x : hilti::rt::to_string(x.error()); +} + +template<> +inline std::string detail::to_string_for_print>(const Result& x) { + return x ? std::string(*x) : hilti::rt::to_string(x.error()); +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/set.h b/hilti/include/rt/types/set.h new file mode 100644 index 000000000..9ce4ade95 --- /dev/null +++ b/hilti/include/rt/types/set.h @@ -0,0 +1,157 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * A set that mostly builds on std::set, but adds a couple of things: + * + * - We add safe HILTI-side iterators become detectably invalid when the main + * containers gets destroyed. + * + * - [Future] Automatic element expiration. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace hilti::rt { + +template +class Set; + +namespace set { + +template +class SafeIterator + : public hilti::rt::detail::iterator::SafeIterator, typename Set::SafeIterator, SafeIterator> { +public: + using Base = hilti::rt::detail::iterator::SafeIterator, typename Set::SafeIterator, SafeIterator>; + using Base::Base; +}; + +template +class SafeConstIterator : public hilti::rt::detail::iterator::SafeIterator, typename Set::ConstIterator, + SafeConstIterator> { +public: + using Base = + hilti::rt::detail::iterator::SafeIterator, typename Set::ConstIterator, SafeConstIterator>; + using Base::Base; +}; + +} // namespace set + +/** HILTI's `Set` is an extended version `std::set`. */ +template +class Set : public std::set, public hilti::rt::detail::iterator::Controllee { +public: + using V = std::set; + using C = hilti::rt::detail::iterator::Controllee; + + using ConstIterator = typename V::const_iterator; + using SafeIterator = typename V::iterator; + + Set() = default; + Set(const Set&) = default; + Set(Set&&) noexcept = default; + Set(const std::list& l) : std::set(l.begin(), l.end()) {} + Set(std::list&& l) : std::set(std::move_iterator(l.begin()), std::move_iterator(l.end())) {} + ~Set() = default; + + Set& operator=(const Set&) = default; + Set& operator=(Set&&) noexcept = default; + + /** Returns true if a specific element is part of the set. */ + bool contains(const T& t) { return this->find(t) != this->end(); } +}; + +namespace set { +/** Place-holder type for an empty set that doesn't have a known element type. */ +class Empty : public Set {}; + +template +inline bool operator==(const Set& v, const Empty& /*unused*/) { + return v.empty(); +} +template +inline bool operator==(const Empty& /*unused*/, const Set& v) { + return v.empty(); +} +template +inline bool operator!=(const Set& v, const Empty& /*unused*/) { + return ! v.empty(); +} +template +inline bool operator!=(const Empty& /*unused*/, const Set& v) { + return ! v.empty(); +} +} // namespace set + +namespace detail::adl { +template +inline std::string to_string(const Set& x, adl::tag /*unused*/) { + return fmt("{%s}", rt::join(rt::transform(x, [](const std::optional& y) { return rt::to_string(y); }), ", ")); +} + +inline std::string to_string(const set::Empty& x, adl::tag /*unused*/) { return "{}"; } + +template +inline std::string to_string(const set::SafeIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline std::string to_string(const set::SafeConstIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline auto safe_begin(const Set& x, adl::tag /*unused*/) { + return set::SafeConstIterator(x, x.begin()); +} + +template +inline auto safe_begin(Set& x, adl::tag /*unused*/) { + return set::SafeIterator(x, x.begin()); +} + +template +inline auto safe_end(const Set& x, adl::tag /*unused*/) { + return set::SafeConstIterator(x, x.end()); +} + +template +inline auto safe_end(Set& x, adl::tag /*unused*/) { + return set::SafeIterator(x, x.end()); +} + +} // namespace detail::adl + +template +inline std::ostream& operator<<(std::ostream& out, const Set& x) { + out << to_string(x); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const set::Empty& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const set::SafeIterator& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const set::SafeConstIterator& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/stream.h b/hilti/include/rt/types/stream.h new file mode 100644 index 000000000..3c5749d59 --- /dev/null +++ b/hilti/include/rt/types/stream.h @@ -0,0 +1,966 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +// TODO(robin): These classes need a cleanup. The current structure is still an +// artifact of previousky having just a single type for bytes and streams. + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace hilti::rt { + +class Bytes; + +class Stream; +namespace stream { +class View; +} // namespace stream +namespace stream { +class SafeConstIterator; +} // namespace stream + +namespace detail::adl { +extern std::string to_string(const Stream& x, adl::tag /*unused*/); +extern std::string to_string(const stream::View& x, adl::tag /*unused*/); +extern std::string to_string(const stream::SafeConstIterator& x, adl::tag /*unused*/); +} // namespace detail::adl + +namespace stream { + +/** A single element inside a stream instance. */ +using Byte = uint8_t; + +/** Offset within a stream instance. */ +using Offset = integer::safe; + +/** Size of a stream instance in numer of elements stores. */ +using Size = integer::safe; + +/** + * Exception reflecting an attempt to modify a stream object that's been frozen. + */ +HILTI_EXCEPTION(Frozen, RuntimeError) + +namespace detail { + +class UnsafeConstIterator; + +/** + * One block of continous data inside a stream instace. A stream instance + * chains these to represent all of its content. + */ +class Chunk { +public: + static const int SmallBufferSize = 32; + using Array = std::pair>; + using Vector = std::vector; + + Chunk() : _data(Array()) {} + Chunk(Offset o, std::array&& d, Size n) : _offset(o), _data(std::make_pair(n, d)) {} + Chunk(Offset o, Vector&& d) : _offset(o), _data(std::move(d)) {} + Chunk(const View& d); + Chunk(const std::string& s); + Chunk(const Chunk& other) : _offset(other._offset), _data(other._data) {} + Chunk(Chunk&& other) noexcept : _offset(other._offset), _data(std::move(other._data)) {} + ~Chunk() = default; + + Chunk& operator=(const Chunk& other) { + _offset = other._offset; + _data = other._data; + return *this; + } + Chunk& operator=(Chunk&& other) noexcept { + _offset = other._offset; + _data = std::move(other._data); + return *this; + } + + Offset offset() const { return _offset; } + bool isCompact() const { return std::holds_alternative(_data); } + + const Byte* begin() const { + if ( auto a = std::get_if(&_data) ) + return a->second.data(); + + auto& v = std::get(_data); + return v.data(); + } + + const Byte* end() const { + if ( auto a = std::get_if(&_data) ) + return a->second.data() + a->first.Ref(); + + auto& v = std::get(_data); + return v.data() + v.size(); + } + + Size size() const { + if ( auto a = std::get_if(&_data) ) + return a->first; + + auto& v = std::get(_data); + return v.size(); + } + + Byte at(Offset o) const { return *data(o); } + + const Byte* data(Offset o) const { + auto c = this; + + while ( o < c->_offset || o >= c->_offset + c->size() ) { + if ( ! c->_next ) + throw InvalidIterator("offset outside of valid range (1)"); + + c = c->next().get(); + } + + return c->begin() + (o - c->_offset).Ref(); + } + + void freeze() { _frozen = true; } + void unfreeze() { _frozen = false; } + bool isFrozen() const { return _frozen; } + + bool isLast() const { return _next == nullptr; } + const std::shared_ptr& next() const { return _next; } + auto last() const { + std::shared_ptr i = _next; + while ( i && i->_next ) + i = i->_next; + return i; + } + + void clearNext() { _next = nullptr; } + void setNext(std::shared_ptr c) { _next = std::move(c); } + void setOffset(Offset o) { _offset = o; } + bool tryAppend(const Chunk& d); // Appends to small buffer is possible, returns false of not. + void trim(Offset o); + + void debugPrint(std::ostream& out) const; + +private: + Byte* begin() { + if ( auto a = std::get_if(&_data) ) + return a->second.data(); + + auto& v = std::get(_data); + return v.data(); + } + + // Note: We must not have a pointer to the parent stream instance in + // chunks because the parent may be on the stack with a shorter life + // time. + Offset _offset = 0; + std::variant _data; + std::shared_ptr _next = nullptr; + bool _frozen = false; + + // TODO(robin): Implement later. + // std::optional object; + // std::vector marks; // offsets relative to this chunk +}; + +/** The main content structure for a heap-allocated stream object. */ +struct Chain { + std::shared_ptr head; + std::shared_ptr tail; + + Chain(Chunk&& ch) : head(std::make_shared(std::move(ch))), tail(head) {} + Chain(const std::string& data) : head(std::make_shared(data)), tail(head) {} + Chain(std::shared_ptr&& head, std::shared_ptr&& tail) + : head(std::move(head)), tail(std::move(tail)) {} +}; + +} // namespace detail + +/** + * SafeConstIterator for traversing the content of a stream instance. + * + * Unlike the standard `Iterator`, this iterator version protects against the + * stream instance being no longer available by throwing an `InvalidIterator` + * exception if it's still accessed. + * + * A safe iterator can also be advanced beyond the end of a stream instead. + * If the instance gets expanded later, the iterator will be refer to that + * new data. + */ +class SafeConstIterator { +public: + SafeConstIterator() = default; + + /** Returns the offset inside the stream that iterator represents. */ + Offset offset() const { return _offset; } + + /** Returns true if the stream instance that the iterator is bound to has been frozen. */ + bool isFrozen() const { return chunk()->isFrozen(); } + + /** + * Returns an iterator corresponding to the end position of the + * underlying stream object. + */ + SafeConstIterator end() const { + check(); + + SafeConstIterator e; + + if ( isEnd() ) + e = *this; + else if ( chunk()->isLast() ) + e = {_content, chunk()->offset() + chunk()->size(), _chunk}; + else { + auto l = chunk()->last(); + assert(l); + assert(l->isLast()); + e = {_content, l->offset() + l->size(), l}; + } + + assert(e.isEnd()); + return e; + } + + /** Advances the iterator by one byte. */ + auto& operator++() { + check(); + increment(1); + return *this; + } + + /** Advances the iterator by one byte. */ + auto operator++(int) { // NOLINT + auto x = *this; + increment(1); + return x; + } + + /** Advances the iterator by a given number of stream. */ + auto& operator+=(integer::safe i) { + check(); + increment(i); + return *this; + } + + /** Returns the character at the iterator's position. */ + auto operator*() const { + check(); + return dereference(); + } + + /** Return a new iterator advanced by a given number of stream. */ + auto operator+(integer::safe i) const { return SafeConstIterator(*this) += i; } + + /** + * Return the size of the range defined by the two iterators. The result + * will be negative if the instances's location comes before the + * arguments's location. + */ + integer::safe operator-(const SafeConstIterator& other) const { + return static_cast(offset()) - static_cast(other.offset()); + } + + /** + * Returns true if another iterator bound to the same stream instance + * refers to the same location. The result is undefined if the iterators + * aren't refering to the same stream instance. + */ + bool operator==(const SafeConstIterator& other) const { + check(); + other.check(); + return (_offset == other._offset) || (isEnd() && other.isEnd()); + } + + /** + * Returns true if another iterator bound to the same stream instance does + * not refer to the same location. The result is undefined if the + * iterators aren't refering to the same stream instance. + */ + bool operator!=(const SafeConstIterator& other) const { return ! (*this == other); } + + /** Compares the offset of two iterators refering to the same stream instance. */ + bool operator<(const SafeConstIterator& other) const { return offset() < other.offset(); } + + /** Compares the offset of two iterators refering to the same stream instance. */ + bool operator<=(const SafeConstIterator& other) const { return offset() <= other.offset(); } + + /** Compares the offset of two iterators refering to the same stream instance. */ + bool operator>(const SafeConstIterator& other) const { return offset() > other.offset(); } + + /** Compares the offset of two iterators refering to the same stream instance. */ + bool operator>=(const SafeConstIterator& other) const { return offset() >= other.offset(); } + + /** Returns true if the iterator is bound to a stream instance. */ + explicit operator bool() const { return ! isUnset(); } + + std::ostream& operator<<(std::ostream& out) const { + out << to_string(*this); + return out; + } + + void* chain() const { return content(); } + + /** Returns true if the iterator remains unintialized. */ + bool isUnset() const { + const auto unset = std::weak_ptr(); + return ! (_content.owner_before(unset) || unset.owner_before(_content)); + } + + /** Returns true if the iterator is at or beyond the current end of the underlying stream instance. */ + bool isEnd() const { return (! chunk()) || (chunk()->isLast() && _offset >= chunk()->offset() + chunk()->size()); } + + /** + * Returns true if the iterator was once valid but the underlying btes + * instance has by now expired. + */ + bool isExpired() const { + normalize(); + + if ( ! _chunk.expired() ) + return false; + + if ( isUnset() ) + return false; + + if ( content()->head && _offset >= content()->head->offset() ) + return false; + + return true; + } + + void debugPrint(std::ostream& out) const; + +private: + friend class hilti::rt::Stream; + friend class hilti::rt::stream::View; + friend class hilti::rt::stream::detail::UnsafeConstIterator; + + SafeConstIterator(std::weak_ptr content, Offset offset, std::weak_ptr chunk) + : _content(std::move(content)), _offset(offset), _chunk(std::move(chunk)) { + // Make sure _content is set. + assert(! isUnset()); + } + + detail::Chunk* chunk() const { + normalize(); + return _chunk.lock().get(); + } + detail::Chain* content() const { + normalize(); + return _content.lock().get(); + } + + void check() const { + normalize(); + + if ( ! _chunk.expired() ) + return; + + if ( isUnset() ) + throw InvalidIterator("not initialized"); + + if ( _content.use_count() == 0 ) + throw InvalidIterator("deleted stream object"); + + throw InvalidIterator("invalidated iterator"); + } + + void normalize() const { + if ( ! isUnset() ) { + if ( auto content = _content.lock().get(); content && content->head && _offset >= content->head->offset() ) + // New in-range data was appended but current chunk is expired. + // Reinit from beginning of stream data. + _chunk = content->head; + } + + while ( auto chunk = _chunk.lock().get() ) { + if ( chunk->isLast() || _offset < chunk->offset() + chunk->size() ) + break; + + _chunk = chunk->next(); + } + } + + void increment(integer::safe n) { + _offset += n; + normalize(); + } + + Byte dereference() const { return chunk()->at(_offset); } + + std::weak_ptr _content; // Parent stream object. + Offset _offset = 0; // Offset inside parent stream object. + mutable std::weak_ptr _chunk; // Current chunk +}; + +inline std::ostream& operator<<(std::ostream& out, const SafeConstIterator& x) { + out << to_string(x); + return out; +} + +namespace detail { + +/** + * Standard iterator for internal usage. Unlike `SafeConstIterator`, this iterator + * version is not safe against the underlying stream instances disappearing; + * it will not catch that and likely cause a crash. When using this, one + * hence needs to ensure that the stream instance will remain valid for at + * least as long as the iterator (like with any standard C++ container). In + * return, this iterator is more efficient than the `SafeConstIterator`. + */ +class UnsafeConstIterator { +public: + UnsafeConstIterator() = default; + explicit UnsafeConstIterator(const SafeConstIterator& i) { + auto x = i; + i.normalize(); + _content = i._content; + _offset = i.offset(); + _shadow_chunk = i._chunk.lock(); + _chunk = _shadow_chunk.get(); + } + + Offset offset() const { return _offset; } + + /** Provides direct access to the current chunk. */ + const Chunk* chunk() const { return _chunk; } + + auto& operator++() { + increment(1); + return *this; + } + + auto operator++(int) { // NOLINT + const auto x = *this; + increment(1); + return x; + } + + auto& operator+=(integer::safe i) { + increment(i); + return *this; + } + + auto operator*() const { + assert(_chunk); + return _chunk->at(_offset); // NOLINT + } + auto operator+(integer::safe i) const { return (UnsafeConstIterator(*this) += i); } + + bool operator==(const UnsafeConstIterator& other) const { + return (_offset == other._offset) || (isEnd() && other.isEnd()); + } + + bool operator==(const SafeConstIterator& other) const { + return (_offset == other._offset) || (isEnd() && other.isEnd()); + } + + bool operator!=(const UnsafeConstIterator& other) const { return ! (*this == other); } + bool operator!=(const SafeConstIterator& other) const { return ! (*this == other); } + explicit operator bool() const { return _chunk != nullptr; } + + explicit operator SafeConstIterator() const { + if ( ! _shadow_chunk ) + throw InvalidIterator("illegal iterator conversion"); + + if ( ! _content.use_count() ) + hilti::rt::internalError("cannot convert stream::Iterator to stream::SafeConstIterator"); + + return SafeConstIterator{_content, _offset, _shadow_chunk}; + } + + bool isEnd() const { return (! chunk()) || (chunk()->isLast() && _offset >= chunk()->offset() + chunk()->size()); } + + void debugPrint(std::ostream& out) const; + +private: + friend class hilti::rt::Stream; + UnsafeConstIterator(std::weak_ptr content, Offset offset, const detail::Chunk* chunk) + : _content(std::move(content)), _offset(offset), _chunk(chunk) {} + + void increment(integer::safe n) { + _offset += n; + + while ( _chunk && ! _chunk->isLast() && _offset >= _chunk->offset() + _chunk->size() ) { + _chunk = _chunk->next().get(); + if ( _shadow_chunk ) + _shadow_chunk = _shadow_chunk->next(); + } + } + + std::weak_ptr _content; // Parent stream object. + Offset _offset = 0; + std::shared_ptr _shadow_chunk; + const detail::Chunk* _chunk = nullptr; +}; + +} // namespace detail + +namespace detail { +template +inline UnsafeConstIterator extract(Byte* dst, const UnsafeConstIterator& i, const SafeConstIterator& end) { + if ( i == end ) + throw WouldBlock("end of stream views"); + + *dst = *i; + return extract(dst + 1, i + 1, end); +} + +template<> +inline UnsafeConstIterator extract<0>(Byte* /* dst */, const UnsafeConstIterator& i, + const SafeConstIterator& /* end */) { + return i; +} + +} // namespace detail + +/** + * A subrange of stream instance. The view is maintained through two safe + * iterators; no data is copied. That makes the view cheap to create and pass + * around. Because of the use of safe containers, it'll be caught through + * `InvalidIterator` if the underlying stream instances goes away. + */ +class View { +public: + View() = default; + View(SafeConstIterator begin, SafeConstIterator end) : _begin(std::move(begin)), _end(std::move(end)) {} + + /** + * Will always reflect a view to the end of the underlying stream object, + * including when that expands. + */ + explicit View(SafeConstIterator begin) : _begin(std::move(begin)) {} + + /** + * Returns the offset of the view's starting location within the associated + * stream instance. + */ + Offset offset() const { return _begin.offset(); } + + /** Returns the number of bytes spanned by the view. */ + Size size() const; + + /** Returns true if the view's size is zero. */ + bool isEmpty() const { return size() == 0; } + + /** Returns true if the instance is currently frozen. */ + bool isFrozen() const { return _begin.isFrozen(); } + + /** XXX */ + bool isOpenEnded() const { return ! _end.has_value(); } + + /** + * Returns the position of the first occurence of a byte inside the view. + * + * @param b byte to search + * @param n optional starting point, which must be inside the vier + */ + SafeConstIterator find(Byte b, const SafeConstIterator& n = SafeConstIterator()) const; + + /** + * Searches for the first occurence of another view's data. + * + * @param v data to search for + * @param n optional starting point, which must be inside this view + * @return tuple where the 1st element is a boolean indicating whether + * *v* has been found; if yes, the 2nd element points to the 1st stream; + * if no, the 2nd element points to the first byte so that no earlier + * position has even a partial match of *v*. + */ + std::tuple find(const View& v, const SafeConstIterator& n = SafeConstIterator()) const; + + /** + * Searches for the first occurence of data. + * + * @param v data to search for + * @param n optional starting point, which must be inside this view + * @return tuple where the 1st element is a boolean indicating whether + * *v* has been found; if yes, the 2nd element points to the 1st byte; + * if no, the 2nd element points to the first byte so that no earlier + * position has even a partial match of *v*. + */ + std::tuple find(const Bytes& v, const SafeConstIterator& n = SafeConstIterator()) const; + + /** + * Advances the view's starting position to a new place. + * + * @param i the new position, which must be inside the current view + * @return the modified view + */ + View advance(SafeConstIterator i) const { return View(std::move(i), _end); } + + /** + * Advances the view's starting position by a given number of stream. + * + * @param i the number of stream to advance. + */ + View advance(integer::safe i) const { return View(safeBegin() + i, _end); } + + /** + * Extracts a subrange of bytes from the view, returned as a new view. + * + * @param from iterator pointing to start of subrange + * @param to iterator pointing to just beyond subrange + */ + View sub(SafeConstIterator from, SafeConstIterator to) const { return View(std::move(from), std::move(to)); } + + /** + * Extracts subrange of bytes from the beginning of the view, returned as + * a new view. + * + * @param to iterator pointing to just beyond subrange + */ + View sub(SafeConstIterator to) const { return View(safeBegin(), std::move(to)); } + + /** + * Extracts subrange of bytes from the view, returned as a new view. + * + * @param offset of start of subrage, relative to beginning of view + * @param offset of one byeond end of subrage, relative to beginning of view + */ + View sub(Offset from, Offset to) const { return View(safeBegin() + from, safeBegin() + to); } + + /** + * Extracts subrange of stream from the beginning of the view, returned as + * a new view. + * + * @param to of one byeond end of subrange, relative to beginning of view + */ + View sub(Offset to) const { return View(safeBegin(), safeBegin() + to); } + + /** Returns an iterator representing an offset inside the view's data */ + SafeConstIterator at(Offset offset) const { return safeBegin() + (offset - safeBegin().offset()); } + + /** + * Returns a new view moves the beginning to a subsequent iterator while + * not changing the end. In particluar, this maintains a view capapbility + * to expand to an underlying data instance's growth. + */ + View trim(const SafeConstIterator& nbegin) const { return _end ? View(nbegin, *_end) : View(nbegin); } + + /** + * Returns a new view that keeps the current start but cuts off the end + * at a specified offset from that beginning. The returned view will not + * be able to expand any further. + */ + View limit(Offset incr) const { return View(safeBegin(), safeBegin() + incr); } + + /** + * Extracts a fixed number of stream from the view. + * + * @tparam N number of stream to extract + * @param dst attry to writes stream into + * @return new view that has it's starting position advanced by N + */ + template + View extract(Byte (&dst)[N]) const { + return View(SafeConstIterator(detail::extract(dst, detail::UnsafeConstIterator(_begin), safeEnd())), _end); + } + + /** + * Copies the view into raw memory + * + * @param dst destination to write to, which must have at least `size()` + * stream available(). + */ + void copyRaw(Byte* dst) const; + + /** Returns a copy of the data the view refers to. */ + std::string data() const; + + detail::UnsafeConstIterator begin() const { + _begin.check(); + return detail::UnsafeConstIterator(_begin); + } + + detail::UnsafeConstIterator end() const { return detail::UnsafeConstIterator(safeEnd()); } + + const SafeConstIterator& safeBegin() const { return _begin; } + SafeConstIterator safeEnd() const { return _end ? *_end : _begin.end(); } + + void* chain() const { return _begin.chain(); } + + /** State for block-wise iteration of a stream instance. */ + struct Block { + const Byte* start; /**< Pointer to first byte. */ + uint64_t size; /**< Number of stream in block. */ + uint64_t offset; /**< Offset of first byte. */ + bool is_first; /**< true if first block visited during iteration. */ + bool is_last; /**< true if last block that will be visited during iteration. */ + const detail::Chunk* _block; /**< Internal use only. */ + }; + + /** + * Initialization method for block-wise iteration over raw data. + */ + std::optional firstBlock() const; + + /** + * Iterates to next block during block-wise iteration over raw data. + */ + std::optional nextBlock(std::optional current) const; + + /** + * Returns true if the view's data begins with a given, other stream + * instance. + */ + bool startsWith(const Bytes& b) const; + + bool operator==(const Bytes& other) const; + bool operator==(const Stream& other) const; + bool operator==(const View& other) const; + bool operator!=(const Bytes& other) const { return ! (*this == other); } + bool operator!=(const Stream& other) const { return ! (*this == other); } + bool operator!=(const View& other) const { return ! (*this == other); } + + void debugPrint(std::ostream& out) const; + +private: + View(SafeConstIterator begin, std::optional end) + : _begin(std::move(begin)), _end(std::move(end)) {} + + SafeConstIterator _begin; + std::optional _end; +}; + +inline std::ostream& operator<<(std::ostream& out, const View& x) { + out << x.data(); + return out; +} + +} // namespace stream + +/** + * Container for raw binary data that's going to be processed in streaming + * mode.. The underlying data storage is optimized for cheap append + * operations even with large instances, but does not allow for modifications + * of existing data. It also ensures that iterators bound to an instace can + * reliably detect if the instance gets deleted. + */ +class Stream { +private: + using Chunk = stream::detail::Chunk; + using Byte = stream::Byte; + using Offset = stream::Offset; + using Size = stream::Size; + +public: + using const_iterator = stream::SafeConstIterator; + + Stream() : Stream(Chunk("")) {} + + /** Creates an instance from a vector of stream. */ + explicit Stream(std::vector d) : Stream(Chunk(0, std::move(d))) {} + + /** Creates an instance from a bytes instance. */ + explicit Stream(const Bytes& d); + + /** Creates an instance for C-style ASCIIZ string, not including the final null byte. The data will be copied. */ + explicit Stream(const char* d) : Stream(chunkFromArray(0, d, strlen(d))) {} + + /** Creates an instance from an existing memory block. The data will be copied. */ + Stream(const char* d, Size n) : Stream(chunkFromArray(0, d, n)) {} + + /** Creates an instance from an existing stream view. */ + Stream(const stream::View& d) : Stream(Chunk(d)) {} + + /** Creates an instance from a series of static-sized blocks. */ + template + Stream(std::vector> d) : Stream(chunkFromArray(0, std::move(d))) {} + + Stream(const Stream& other) noexcept : _content(other.deepCopyContent()), _frozen(other._frozen) {} + Stream(Stream&& other) noexcept : _content(std::move(other._content)), _frozen(other._frozen) {} + + Stream& operator=(Stream&& other) noexcept { + if ( &other == this ) + return *this; + + _content = std::move(other._content); + _frozen = other._frozen; + return *this; + } + + Stream& operator=(const Stream& other) { + if ( &other == this ) + return *this; + + _content = other.deepCopyContent(); + _frozen = other._frozen; + return *this; + } + + ~Stream() = default; + + /** Returns the number of stream the instance contains. */ + Size size() const { return tail()->offset() + tail()->size() - head()->offset(); } + + /** Returns true if the instance's size is zero. */ + bool isEmpty() const { return size() == 0; } + + /** For internal debugging: Returns the number of dynamic chunbks allocated. */ + int numberChunks() const; + + /** Appends the content of a bytes instance. */ + void append(const Bytes& data); + + /** Appends the content of a bytes instance. */ + void append(Bytes&& data); + + /** Appends the content of a raw memory area, taking ownership. */ + void append(std::unique_ptr data); + + /** Appends the content of a raw memory area, copying the data. */ + void append(const char* data, size_t len); + + /** + * Cuts off the beginning of the data up to, but excluding, a given + * iterator. All existing iterators pointing beyond that point will + * remain valid and keep their offsets the same. Trimming is permitted + * even on frozen instances. + */ + void trim(const stream::SafeConstIterator& i); + + /** Freezes the instance. When frozen, no further data can be appended. */ + void freeze(); + + /** Unfreezes the instance so that more data can be appended again. */ + void unfreeze(); + + /** Returns true if the instance is currently frozen. */ + bool isFrozen() const { return _frozen; } + + /** Returns an interator representing the first byte of the instance. */ + stream::SafeConstIterator safeBegin() const { return {_content, _content->head->offset(), _content->head}; } + + /** Returns an interator representing the end of the instance. */ + stream::SafeConstIterator safeEnd() const { + auto& t = _content->tail; + return {_content, t->offset() + t->size(), t}; + } + + /** Returns an interator representing a specific offset. */ + stream::SafeConstIterator at(Offset offset) const { return safeBegin() + (offset - safeBegin().offset()); } + + /** + * Returns a view representing the entire instance. + * + * @param expanding if true, the returned view will automatically grow + * along with the stream object if more data gets added. + */ + stream::View view(bool expanding = true) const { + if ( expanding ) + return stream::View(safeBegin()); + + return stream::View(safeBegin(), safeEnd()); + } + + stream::detail::UnsafeConstIterator begin() const { return {{}, head()->offset(), head()}; } + stream::detail::UnsafeConstIterator end() const { + auto t = tail(); + return {{}, t->offset() + t->size(), t}; + } + + /** Returns a copy of the data the stream refers to. */ + std::string data() const; + + bool operator==(const Bytes& other) const { return view() == other; } + bool operator==(const Stream& other) const { return view() == other.view(); } + bool operator==(const stream::View& other) const { return view() == other; } + bool operator!=(const Bytes& other) const { return ! (*this == other); } + bool operator!=(const Stream& other) const { return ! (*this == other); } + bool operator!=(const stream::View& other) const { return ! (*this == other); } + + void debugPrint(std::ostream& out) const; + static void debugPrint(std::ostream& out, const stream::detail::Chain* chain); + +private: + friend class stream::View; + using ChainPtr = std::shared_ptr; + using Content = ChainPtr; + using UnsafeConstIterator = stream::detail::UnsafeConstIterator; + + Stream(Chunk&& ch) : _content(std::make_shared(std::move(ch))) {} + + const Chunk* head() const { return _content->head.get(); } + + Chunk* head() { return _content->head.get(); } + + const Chunk* tail() const { return _content->tail.get(); } + + Chunk* tail() { return _content->tail.get(); } + + int compare(const Stream& other) const { return compare(begin(), end(), other.begin(), other.end()); } + + static int compare(UnsafeConstIterator s1, const UnsafeConstIterator& e1, UnsafeConstIterator s2, + const UnsafeConstIterator& e2); + + template + inline Chunk chunkFromArray(Offset o, std::array d) { + if constexpr ( N <= Chunk::SmallBufferSize ) + return Chunk(o, std::move(d)); + + return Chunk(o, Chunk::Vector(d.begin(), d.end())); + } + + inline Chunk chunkFromArray(Offset o, const char* d, Size n) { + auto ud = reinterpret_cast(d); + + if ( n <= Chunk::SmallBufferSize ) { + std::array x{}; + std::copy(ud, ud + n.Ref(), x.data()); + return Chunk(o, std::move(x), n); // NOLINT + } + + return Chunk(o, Chunk::Vector(ud, ud + n.Ref())); + } + + void appendContent(Content&& ocontent); + Content deepCopyContent() const; + + Content _content; + bool _frozen = false; +}; + +inline std::ostream& operator<<(std::ostream& out, const Stream& x) { + out << x.data(); + return out; +} + +template<> +inline std::string detail::to_string_for_print(const Stream& x) { + return escapeUTF8(x.data(), true); +} + +template<> +inline std::string detail::to_string_for_print(const stream::View& x) { + return escapeUTF8(x.data(), true); +} + +namespace detail::adl { +inline auto safe_begin(const Stream& x, adl::tag /*unused*/) { return x.safeBegin(); } +inline auto safe_end(const Stream& x, adl::tag /*unused*/) { return x.safeEnd(); } +inline auto safe_begin(const stream::View& x, adl::tag /*unused*/) { return x.safeBegin(); } +inline auto safe_end(const stream::View& x, adl::tag /*unused*/) { return x.safeEnd(); } +inline std::string to_string(const Stream& x, adl::tag /*unused*/) { + return fmt("b\"%s\"", escapeUTF8(x.data(), true)); +} +inline std::string to_string(const stream::View& x, adl::tag /*unused*/) { + return fmt("b\"%s\"", escapeUTF8(x.data(), true)); +} +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/string.h b/hilti/include/rt/types/string.h new file mode 100644 index 000000000..16dcf8f4f --- /dev/null +++ b/hilti/include/rt/types/string.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +#include +#include + +namespace hilti::rt { + +namespace string { + +/** Returns the length of a UTF8 string. */ +size_t size(const std::string& s); + +/** Returns a lower-case version of an UTF8 string. */ +std::string lower(const std::string& s); + +/** Returns an upper-case version of an UTF8 string. */ +std::string upper(const std::string& s); + +} // namespace string + +namespace detail::adl { +inline std::string to_string(const std::string& x, adl::tag /*unused*/) { return fmt("\"%s\"", escapeUTF8(x, true)); } + +inline std::string to_string(std::string_view x, adl::tag /*unused*/) { return fmt("\"%s\"", escapeUTF8(x, true)); } + +} // namespace detail::adl + +template<> +inline std::string detail::to_string_for_print(const std::string& x) { + return escapeUTF8(x, false, false); +} + +template<> +inline std::string detail::to_string_for_print(const std::string_view& x) { + return escapeUTF8(x, false, false); +} + + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/struct.h b/hilti/include/rt/types/struct.h new file mode 100644 index 000000000..5a9f006d2 --- /dev/null +++ b/hilti/include/rt/types/struct.h @@ -0,0 +1,54 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti::rt { + +namespace trait { +struct isStruct {}; +struct hasParameters {}; +} // namespace trait + +/** + * Exception triggered y the ".?" operator to signal to host applications that + * a struct attribbute isn't set. + */ +HILTI_EXCEPTION(AttributeNotSet, Exception) + +namespace struct_ { + +template +inline auto& value_or_exception(const std::optional& t, const char* location) { + if ( t.has_value() ) + return t.value(); + + throw AttributeNotSet("struct attribute not set", location); +} +} // namespace struct_ + +namespace detail::adl { +template::value>* = nullptr> +inline std::string to_string(const T& x, adl::tag /*unused*/) { + std::string fields; + bool first = true; + + auto render_one = [&](auto k, auto v) { + if ( ! first ) + fields += ", "; + else + first = false; + + fields += fmt("$%s=%s", k, hilti::rt::to_string(v)); + }; + + x.__visit(render_one); + return fmt("[%s]", fields); +} + +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/time.h b/hilti/include/rt/types/time.h new file mode 100644 index 000000000..9f7dac3b2 --- /dev/null +++ b/hilti/include/rt/types/time.h @@ -0,0 +1,104 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +#include +#include + +namespace hilti::rt { + +/** + * Represents HILTI's time type. Intervals are stored as nanoseconds + * resolution as intervals since the UNIX epoch. A value of zero represents + * an unset time. + */ +class Time { +public: + /** + * Constructs a time from a nanoseconds value. + * + * @param nsecs nanonseconds since the UNIX epoch. + */ + explicit Time(uint64_t nsecs = 0) : _nsecs(nsecs) {} + + /** + * Constructs an interval from an unsigned integer value. + * + * @param nsecs interval in nanoseconds. + */ + explicit Time(hilti::rt::integer::safe nsecs) : _nsecs(nsecs) {} + + /** + * Constructs a time from a double value. + * + * @param secs seconds since the UNIX epoch. + */ + explicit Time(double secs) : _nsecs(static_cast(secs * 1e9)) {} + + /** Constructs an unset time. */ + Time(const Time&) = default; + Time(Time&&) noexcept = default; + ~Time() = default; + + Time& operator=(const Time&) = default; + Time& operator=(Time&&) noexcept = default; + + /** Returns a UNIX timestmap. */ + double seconds() const { return _nsecs / 1e9; } + + /** Returns nanosecs since epoch. */ + uint64_t nanoseconds() const { return _nsecs; } + + bool operator==(const Time& other) const { return _nsecs == other._nsecs; } + bool operator!=(const Time& other) const { return _nsecs != other._nsecs; } + bool operator<(const Time& other) const { return _nsecs < other._nsecs; } + bool operator<=(const Time& other) const { return _nsecs <= other._nsecs; } + bool operator>(const Time& other) const { return _nsecs > other._nsecs; } + bool operator>=(const Time& other) const { return _nsecs >= other._nsecs; } + + Time operator+(const Interval& other) const { + if ( other.nanoseconds() < 0 && (static_cast(_nsecs) < (-other.nanoseconds())) ) + throw RuntimeError("operation yielded negative time"); + + return Time(static_cast(_nsecs + other.nanoseconds())); + } + + Time operator-(const Interval& other) const { + if ( static_cast(_nsecs) < other.nanoseconds() ) + throw RuntimeError("operation yielded negative time"); + + return Time(static_cast(_nsecs - other.nanoseconds())); + } + + Interval operator-(const Time& other) const { + return Interval(static_cast(_nsecs) - static_cast(other.nanoseconds())); + } + + /** Returns true if the time is non-zero (i.e., not unset) */ + operator bool() const { return _nsecs == 0.0; } + + /** Returns a human-readable representation of the tiem. */ + operator std::string() const; + +private: + uint64_t _nsecs = 0; // Nanoseconds since epoch}; +}; + +namespace time { +extern Time current_time(); +} // namespace time + +namespace detail::adl { +inline std::string to_string(const Time& x, adl::tag /*unused*/) { return x; } +} // namespace detail::adl + +inline std::ostream& operator<<(std::ostream& out, const Time& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/tuple.h b/hilti/include/rt/types/tuple.h new file mode 100644 index 000000000..00fcca101 --- /dev/null +++ b/hilti/include/rt/types/tuple.h @@ -0,0 +1,29 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace hilti::rt { + +namespace detail::adl { +template::value>* = nullptr> +inline std::string to_string(const T& x, adl::tag /*unused*/) { + auto y = rt::map_tuple(x, [&](auto& v) { return hilti::rt::to_string(v); }); + return fmt("(%s)", rt::join_tuple_for_print(std::move(y))); +} + +template::value>* = nullptr> +inline std::string to_string_for_print(const T& x, adl::tag /*unused*/) { + auto y = rt::map_tuple(x, [&](auto& v) { return hilti::rt::to_string(v); }); + return fmt("(%s)", rt::join_tuple_for_print(std::move(y))); +} +} // namespace detail::adl + +} // namespace hilti::rt + +template::value>* = nullptr> +inline std::ostream& operator<<(std::ostream& out, const T& x) { + return out << hilti::rt::to_string_for_print(x); +} diff --git a/hilti/include/rt/types/union.h b/hilti/include/rt/types/union.h new file mode 100644 index 000000000..304f9d477 --- /dev/null +++ b/hilti/include/rt/types/union.h @@ -0,0 +1,115 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace hilti::rt { + +namespace trait { +struct isUnion {}; +} // namespace trait + +/** + * Exception triggered by member access to fields that don't hold the value. + */ +HILTI_EXCEPTION(UnsetUnionMember, RuntimeError) + +namespace union_ { +namespace detail { + +/** Proxy object to facilitate assignment to a specific variant slot. */ +template +class AssignProxy { +public: + AssignProxy(U* u) : _u(u) {} + + template + AssignProxy& operator=(const T& t) { + _u->value.template emplace(t); + return *this; + } + + template + AssignProxy& operator=(T&& t) { + _u->value.template emplace(std::forward(t)); + return *this; + } + +private: + U* _u; +}; + +} // namespace detail + +template +inline auto& get(const T& u) { + try { + return std::get(u.value); + } catch ( const std::bad_variant_access& ) { + throw UnsetUnionMember("access to union member that does not hold value"); + } +} + +template +inline auto get_proxy(U& u) { + return detail::AssignProxy(&u); +} + +} // namespace union_ + +template +class Union : public trait::isUnion { +public: + Union() = default; + ~Union() = default; + Union(const Union&) = default; + Union(Union&&) noexcept = default; + Union& operator=(const Union&) = default; + Union& operator=(Union&&) noexcept = default; + + template + Union(const F& t) : value(t){}; + template + Union(const F&& t) : value(t){}; + template + Union& operator=(const F& t) { + value = t; + return *this; + } + + template + Union& operator=(const F&& t) { + value = std::move(t); + return *this; + } + + /** + * Returns the index of the value the variant holds. Because we always + * use `std::monostate` as the first type, this will return a value + * greater zero iff a value is set. + */ + auto index() const { return value.index(); } + + std::variant value; +}; + +namespace detail::adl { +template::value>* = nullptr> +inline std::string to_string(const T& x, adl::tag /*unused*/) { + std::string field = ""; + + auto render_one = [&](auto k, auto v) { + if ( v ) + field = fmt("$%s=%s", k, hilti::rt::to_string(*v)); + }; + + x.__visit(render_one); + return field; +} + +} // namespace detail::adl + +} // namespace hilti::rt diff --git a/hilti/include/rt/types/vector.h b/hilti/include/rt/types/vector.h new file mode 100644 index 000000000..e52c28350 --- /dev/null +++ b/hilti/include/rt/types/vector.h @@ -0,0 +1,294 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * A vector that for large part built on std::vector, but adds a couple of things: + * + * - We record if an element has been set at all. + * - We add safe HILTIs-side iterators become detectably invalid when the main + * containers gets destroyed. + * - We add auto-growth on assign. + * - We track which elements are set at all. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace hilti::rt { + +template> +class Vector; + +namespace vector { + +/** + * Allocactor for `Vector` that initializes elements with a given default value. + * + * See https://howardhinnant.github.io/allocator_boilerplate.html and + * https://stackoverflow.com/questions/48061522/create-the-simplest-allocator-with-two-template-arguments + */ +template +class Allocator { +public: + using value_type = T; + + value_type* allocate(std::size_t n) { return static_cast(::operator new(n * sizeof(value_type))); } + + void deallocate(value_type* p, std::size_t) noexcept { ::operator delete(p); } + + template + void construct(U* p) noexcept(std::is_nothrow_default_constructible::value) { + ::new (static_cast(p)) U(Default_); + } + + template + void construct(U* p, Args&&... args) { + ::new (p) U(std::forward(args)...); + } + + template + struct rebind { + using other = Allocator; + }; +}; + +template +bool operator==(Allocator const&, Allocator const&) noexcept { + return true; +} + +template +bool operator!=(Allocator const&, Allocator const&) noexcept { + return false; +} + +template> +class SafeIterator + : public hilti::rt::detail::iterator::SafeIterator< + Vector, typename Vector::SafeIterator, SafeIterator> { +public: + using Base = + hilti::rt::detail::iterator::SafeIterator, typename Vector::SafeIterator, + SafeIterator>; + using Base::Base; +}; + +template> +class SafeConstIterator + : public hilti::rt::detail::iterator::SafeIterator< + const Vector, typename Vector::ConstIterator, SafeConstIterator> { +public: + using Base = hilti::rt::detail::iterator::SafeIterator< + const Vector, typename Vector::ConstIterator, SafeConstIterator>; + using Base::Base; +}; + +} // namespace vector + +// Proxy to faciliate safe assignment. + +/** HILTI's `Vector` is just strong typedef for `std::vector`. */ +template +class Vector : public std::vector, public hilti::rt::detail::iterator::Controllee { +public: + using V = std::vector; + using C = hilti::rt::detail::iterator::Controllee; + + using ConstIterator = typename V::const_iterator; + using SafeIterator = typename V::iterator; + + Vector() = default; + Vector(const Vector&) = default; + Vector(Vector&&) noexcept = default; + Vector(const std::list& l) : std::vector(l.begin(), l.end()) {} + Vector(std::list&& l) : std::vector(std::move_iterator(l.begin()), std::move_iterator(l.end())) {} + ~Vector() = default; + + /** Returns the first element of the vector. */ + const T& front() const { + if ( V::empty() ) + throw IndexError("vector is empty"); + + return V::front(); + } + + /** Returns the last element of the vector. */ + const T& back() const { + if ( V::empty() ) + throw IndexError("vector is empty"); + + return V::back(); + } + + /** Sets an index, auto-growing the vector if necessary. */ + T& set(uint64_t i, T t) { return (V::data()[i] = std::move(t)); } + + Vector& operator=(const Vector&) = default; + Vector& operator=(Vector&&) noexcept = default; + + const T& operator[](uint64_t i) const { + if ( i >= V::size() ) + throw IndexError(fmt("vector index %" PRIu64 " out of range", i)); + + return V::data()[i]; + } + + auto operator[](uint64_t i); + + Vector operator+(const Vector& other) const { + Vector v(*this); + v += other; + return v; + } + + Vector& operator+=(const Vector& other) { + V::insert(V::end(), other.begin(), other.end()); + return *this; + } +}; + +namespace vector::detail { + +template +class AssignProxy { +public: + using V = std::vector; + + AssignProxy(V* v, uint64_t i) : _v(v), _i(i) {} + + // TODO(robin): Not sure why we can't return by reference here, compiler says it + // would be a temporary. + T get() const { + if ( _i >= _v->size() ) + throw IndexError(fmt("vector index %" PRIu64 " out of range", _i)); + + return (*_v)[_i]; + } + + AssignProxy& operator=(T t) { + _v->resize(std::max(static_cast(_v->size()), _i + 1)); + (*_v)[_i] = std::move(t); + return *this; + } + + operator T() const { return get(); } + + bool operator==(const T& t) { return get() == t; } + bool operator!=(const T& t) { return get() != t; } + +private: + V* _v; + uint64_t _i; +}; + +} // namespace vector::detail + +template +inline auto Vector::operator[](uint64_t i) { + return hilti::rt::vector::detail::template AssignProxy(this, i); +} + +template +inline std::ostream& operator<<(std::ostream& out, const hilti::rt::vector::detail::AssignProxy& x) { + out << x.get(); + return out; +} + +namespace vector { +/** Place-holder type for an empty vector that doesn't have a known element type. */ +class Empty : public Vector {}; + +template +inline bool operator==(const Vector& v, const Empty& /*unused*/) { + return v.empty(); +} +template +inline bool operator==(const Empty& /*unused*/, const Vector& v) { + return v.empty(); +} +template +inline bool operator!=(const Vector& v, const Empty& /*unused*/) { + return ! v.empty(); +} +template +inline bool operator!=(const Empty& /*unused*/, const Vector& v) { + return ! v.empty(); +} +} // namespace vector + +namespace detail::adl { +template +inline std::string to_string(const Vector& x, adl::tag /*unused*/) { + return fmt("[%s]", + rt::join(rt::transform(x, [](const std::optional& y) { return (y ? rt::to_string(*y) : "(unset)"); }), + ", ")); +} + +template +inline std::string to_string(const vector::detail::AssignProxy& x, adl::tag /*unused*/) { + return hilti::rt::to_string(x.get()); +} + +inline std::string to_string(const vector::Empty& /* x */, adl::tag /*unused*/) { return "[]"; } + +template +inline std::string to_string(const vector::SafeIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline std::string to_string(const vector::SafeConstIterator& /*unused*/, adl::tag /*unused*/) { + return ""; +} + +template +inline auto safe_begin(const Vector& x, adl::tag /*unused*/) { + return vector::SafeConstIterator(x, x.begin()); +} + +template +inline auto safe_begin(Vector& x, adl::tag /*unused*/) { + return vector::SafeIterator(x, x.begin()); +} + +template +inline auto safe_end(const Vector& x, adl::tag /*unused*/) { + return vector::SafeConstIterator(x, x.end()); +} + +template +inline auto safe_end(Vector& x, adl::tag /*unused*/) { + return vector::SafeIterator(x, x.end()); +} + +} // namespace detail::adl + +template +inline std::ostream& operator<<(std::ostream& out, const Vector& x) { + out << to_string(x); + return out; +} + +inline std::ostream& operator<<(std::ostream& out, const vector::Empty& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const vector::SafeIterator& x) { + out << to_string(x); + return out; +} + +template +inline std::ostream& operator<<(std::ostream& out, const vector::SafeConstIterator& x) { + out << to_string(x); + return out; +} + +} // namespace hilti::rt diff --git a/hilti/include/rt/unpack.h b/hilti/include/rt/unpack.h new file mode 100644 index 000000000..ab6f3e5b7 --- /dev/null +++ b/hilti/include/rt/unpack.h @@ -0,0 +1,5 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +namespace hilti::rt {} // namespace hilti::rt diff --git a/hilti/include/rt/util.h b/hilti/include/rt/util.h new file mode 100644 index 000000000..e864262c9 --- /dev/null +++ b/hilti/include/rt/util.h @@ -0,0 +1,449 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CXX_FILESYSTEM_IS_EXPERIMENTAL +#include +namespace std { +using namespace experimental; +} // namespace std +#else +#include +#endif + +namespace hilti::rt { +void internalError(const std::string& msg) __attribute__((noreturn)); +} // namespace hilti::rt +#undef TINYFORMAT_ERROR +#define TINYFORMAT_ERROR(reason) ::hilti::rt::internalError(reason) +#include +#include +#include + +namespace hilti::rt { + +/** Returns a string identifying the version of the runtime library. */ +extern std::string version(); + +/** Returns true if called for a debug version of the runtime library. */ +extern bool isDebugVersion(); + +/** Dumps a backtrack to stderr and then aborts execution. */ +extern void abort_with_backtrace() __attribute__((noreturn)); + +/** Aborts with an internal error saying we should not be where we are. */ +extern void cannot_be_reached() __attribute__((noreturn)); + +/** Statistics about the current state of memory allocations. */ +struct MemoryStatistics { + // Note when changing this, update `memory_statistucs()`. + uint64_t memory_heap; //< current size of heap in bytes + uint64_t num_fibers; //< number of fibers currently in use + uint64_t max_fibers; //< high-water mark for number of fibers in use + uint64_t cached_fibers; //< number of fibers currently cached for reuse +}; + +/** Returns statistics about the current state of memory allocations. */ +MemoryStatistics memory_statistics(); + +/** + * Returns a string view with all characters of a given set removed. + * + * \note This function is not UTF8-aware. + */ +inline std::string_view rtrim(std::string_view s, const std::string& chars) noexcept { + s.remove_suffix(s.size() - + [](size_t pos) { return pos != std::string_view::npos ? pos + 1 : 0; }(s.find_last_not_of(chars))); + return s; +} + +/** + * Returns a string view with all leading characters of a given set removed. + * + * \note This function is not UTF8-aware. + */ +inline std::string_view ltrim(std::string_view s, const std::string& chars) noexcept { + s.remove_prefix(std::min(s.find_first_not_of(chars), s.size())); + return s; +} + +/** + * Returns a string view with all leading & trailing characters of a given + * set removed. + * + * \note This function is not UTF8-aware. + */ +inline std::string_view trim(std::string_view s, const std::string& chars) noexcept { + return ltrim(rtrim(s, chars), chars); +} + +namespace detail { +constexpr char whitespace_chars[] = " \t\f\v\n\r"; +} // namespace detail + +/** + * Returns a string view with all trailing white space removed. + * + * \note This function is not UTF8-aware. + */ +inline std::string_view rtrim(std::string_view s) noexcept { return rtrim(s, detail::whitespace_chars); } + +/** + * Returns a string view with all leading white space removed. + * + * \note This function is not UTF8-aware. + */ +inline std::string_view ltrim(std::string_view s) noexcept { return ltrim(s, detail::whitespace_chars); } + +/** + * Returns a string view with all leading & trailing white space removed. + * + * \note This function is not UTF8-aware. + */ +inline std::string_view trim(std::string_view s) noexcept { return trim(s, detail::whitespace_chars); } + +/** + * Splits a string at all occurrences of a delimiter. Successive occurences + * of the delimiter will be split into multiple pieces. + * + * \note This function is not UTF8-aware. + */ +std::vector split(std::string_view s, std::string_view delim); + +/** + * Splits a string at all occurrences of successive white space. + * + * \note This function is not UTF8-aware. + */ +std::vector split(std::string_view s); + +/** + * Splits a string once at the 1st occurece of succesive whitespace. Leaves + * the 2nd element of the result pair unset if whitespace does not occur. + * + * \note This function is not UTF8-aware. + */ +extern std::pair split1(std::string s); + +/** + * Splits a string once at the last occurece of succesive whitespace. Leaves + * the 2nd element of the result pair unset if whitespace does not occur. + + * \note This function is not UTF8-aware. + */ +extern std::pair rsplit1(std::string s); + +/** + * Splits a string once at the 1st occurece of a delimiter. Leaves the 2nd + * element of the result pair unset if the delimiter does not occur. + * + * \note This function is not UTF8-aware. + */ +extern std::pair split1(std::string s, const std::string& delim); + +/** + * Splits a string once at the last occurece of a delimiter. Leaves the 1st + * element of the result pair unset if the delimiter does not occur. + * + * \note This function is not UTF8-aware. + */ +extern std::pair rsplit1(std::string s, const std::string& delim); + +/** + * Replaces all occurrences of one string with another. + * + * \note This function is not UTF8-aware. + */ +std::string replace(std::string s, std::string_view o, std::string_view n); + +/** + * Returns true if a string begins with another. + * + * \note This function is not UTF8-aware. + */ +inline bool startsWith(const std::string& s, const std::string& prefix) { return s.find(prefix) == 0; } + +/** + * Python-style enumerate() that teturns an iterable yielding pairs `(index, + * val)`. From http://reedbeta.com/blog/python-like-enumerate-in-cpp17/. + */ +template())), + typename = decltype(std::end(std::declval()))> +constexpr auto enumerate(T&& iterable) { + struct iterator { + size_t i; + TIter iter; + bool operator!=(const iterator& other) const { return iter != other.iter; } + void operator++() { + ++i; + ++iter; + } + auto operator*() const { return std::tie(i, *iter); } + }; + struct iterable_wrapper { + T iterable; + auto begin() { return iterator{0, std::begin(iterable)}; } + auto end() { return iterator{0, std::end(iterable)}; } + }; + return iterable_wrapper{std::forward(iterable)}; +} + +/* + * Expands escape sequences in a UTF8 string. The following escape sequences + * are supported: + * + * ============ ============================ + * Escape Result + * ============ ============================ + * \\ Backslash + * \\n Line feed + * \\r Carriage return + * \\t Tabulator + * \\uXXXX 16-bit Unicode codepoint + * \\UXXXXXXXX 32-bit Unicode codepoint + * \\xXX 8-bit hex value + * ============ ============================ + * + * @param str string to expand + * @return A UTF8 string with escape sequences expanded + */ +std::string expandEscapes(std::string s); + +/* + * Escapes non-printable characters in a raw string. This produces a new + * string that can be reverted by expandEscapes(). + * + * @param str string to escape + * @param escape_quotes if true, also escapes quotes characters + * @param escape_control if false, do not escape control characters + * @return escaped string + */ +std::string escapeBytes(std::string_view s, bool escape_quotes = false, bool escape_control = true); + +/* + * Escapes non-printable and control characters in an UTF8 string. This + * produces a new string that can be reverted by expandEscapes(). + * + * @param str string to escape + * @param escape_quotes if true, also escapes quotes characters + * @param escape_control if false, do not escape control characters + * @return escaped std::string + */ +std::string escapeUTF8(std::string_view s, bool escape_quotes = false, bool escape_control = true); + +/** + * Joins elements of a container into a string, using a specified delimiter + * to separate them. + */ +template +std::string join(const T& l, const std::string& delim = "") { + std::string result; + bool first = true; + + for ( const auto& i : l ) { + if ( not first ) + result += delim; + result += std::string(i); + first = false; + } + + return result; +} + +/** Applies a function to each element of a vector, returning a new + vector with the results. + */ +template +auto transform(const std::vector& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + y.emplace_back(f(i)); + return y; +} + +/** Applies a function to each element of a list, returning a new + vector with the results. + */ +template +auto transform(const std::list& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + y.emplace_back(f(i)); + return y; +} + +/** Applies a function to each element of a set, returning a new + vector with the results. + */ +template +auto transform(const std::set& x, F f) { + using Y = typename std::result_of::type; + std::set y; + for ( const auto& i : x ) + y.insert(f(i)); + return y; +} + +/** Parses a numerical value from a character sequence into an integer. */ +template +inline Iter atoi_n(Iter s, Iter e, int base, Result* result) { + Result n = 0; + bool neg = false; + + if ( s != e && *s == '-' ) { + neg = true; + ++s; + } + + bool first = true; + + for ( ; s != e; s++ ) { + auto c = *s; + unsigned int d = 0; + + if ( isdigit(c) ) + d = c - '0'; + + else if ( c >= 'a' && c < 'a' - 10 + base ) + d = c - 'a' + 10; + + else if ( c >= 'A' && c < 'A' - 10 + base ) + d = c - 'A' + 10; + + else if ( ! first ) + break; + + // else + // throw Exception("cannot decode number"); + + n = n * base + d; + first = false; + } + + if ( neg ) + *result = -n; + else + *result = n; + + return s; +} + +/** + * Computes integer powers + */ +template +inline I1 pow(I1 base, I2 exp) { + I1 x = 1; + + while ( exp ) { + if ( exp & 1 ) + x *= base; + + exp >>= 1; + base *= base; + } + + return x; +} + +// Tuple for-each, from +// https://stackoverflow.com/questions/40212085/type-erasure-for-objects-containing-a-stdtuple-in-c11 +namespace detail { +template +constexpr auto map_tuple(T&& tup, F& f, std::index_sequence /*unused*/) { + return std::make_tuple(f(std::get(std::forward(tup)))...); +} + +template +auto join_tuple(T&& tup, std::index_sequence /*unused*/) { + std::vector x = {rt::to_string(std::get(std::forward(tup)))...}; + return join(x, ", "); +} + +template +auto join_tuple_for_print(T&& tup, std::index_sequence /*unused*/) { + std::vector x = {rt::to_string_for_print(std::get(std::forward(tup)))...}; + return join(x, ", "); +} +} // namespace detail + +/** Generic tuple for-each that runs a callback for each element. */ +template +void tuple_for_each(std::tuple tup, F func) { + if constexpr ( I == sizeof...(Ts) ) + return; + else { + func(std::get(tup)); + tuple_for_each(tup, func); + } +} + +/** + * Applies a transformation function to each element of a tuple, returning a + * new tuple. + */ +template>> +constexpr auto map_tuple(T&& tup, F f) { + return detail::map_tuple(std::forward(tup), f, std::make_index_sequence{}); +} + +/** + * Converts a tuple's elements into string representations and then + * concatenates those with separating commas. This version converts the + * tuple elements into strings using HILTI's standard rendering (which, e.g., + * means that strings will be surrounded by quotes). + */ +template>> +auto join_tuple(T&& tup) { + return detail::join_tuple(std::forward(tup), std::make_index_sequence{}); +} + +/** + * Converts a tuple's elements into string representations and then + * concatenates those with separating commas. This version converts the tuple + * elements into strings as if they were given to a HILTI `print` statements + * (which, e.g., means that top-level strings won't be surrounded by quotes). + */ +template>> +auto join_tuple_for_print(T&& tup) { + return detail::join_tuple_for_print(std::forward(tup), std::make_index_sequence{}); +} + +template +struct is_tuple : std::false_type {}; + +/** Checks if a type is a tuple. */ +template +struct is_tuple> : std::true_type {}; + +template +struct is_optional : std::false_type {}; + +/** Checks if a type is a optional. */ +template +struct is_optional> : std::true_type {}; + +/** Available byte orders. */ +enum class ByteOrder { Little, Big, Network, Host }; + +/** + * Returns the byte order of the system we're running on. The result is + * either `ByteOrder::Little` or `ByteOrder::Big`. + */ +extern ByteOrder systemByteOrder(); + +} // namespace hilti::rt diff --git a/hilti/lib/hilti.hlt b/hilti/lib/hilti.hlt new file mode 100644 index 000000000..545082563 --- /dev/null +++ b/hilti/lib/hilti.hlt @@ -0,0 +1,32 @@ + +module hilti { + +public type BitOrder = enum { LSB0, MSB0 } &cxxname="hilti::rt::integer::BitOrder"; +public type ByteOrder = enum { Little, Big, Network, Host } &cxxname="hilti::rt::ByteOrder"; +public type Side = enum { Left, Right, Both } &cxxname="hilti::rt::bytes::Side"; +public type AddressFamily = enum { IPv4, IPv6 } &cxxname="hilti::rt::AddressFamily"; +public type RealType = enum { IEEE754_Single, IEEE754_Double } &cxxname="hilti::rt::real::Type"; +public type Protocol = enum { TCP, UDP, ICMP } &cxxname="hilti::rt::Protocol"; +public type Charset = enum { ASCII, UTF8} &cxxname="hilti::rt::bytes::Charset"; + +public type MatchState = __library_type("hilti::rt::regexp::MatchState"); + +declare public void print(any obj, bool newline = True) &cxxname="hilti::rt::print"; +declare public void printValues(tuple<*> t, bool newline = True) &cxxname="hilti::rt::printValues"; + +declare public void debug(string dbg_stream, any obj) &cxxname="HILTI_RT_DEBUG"; +declare public void debugIndent(string dbg_stream) &cxxname="hilti::rt::debug::indent"; +declare public void debugDedent(string dbg_stream) &cxxname="hilti::rt::debug::dedent"; + +declare public time current_time() &cxxname="hilti::rt::time::current_time"; + +declare public void abort() &cxxname="hilti::rt::abort_with_backtrace"; + +# Base type for all exceptions. +public type Exception = exception &cxxname="hilti::rt::Exception"; + +# Base type for all exception generated by the runtime system. Catching +# this allows to continue after operations triggering runtime errors. +public type RuntimeError = exception &cxxname="hilti::rt::RuntimeError"; + +} diff --git a/hilti/src/3rdparty/justrx/.gitignore b/hilti/src/3rdparty/justrx/.gitignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/hilti/src/3rdparty/justrx/.gitignore @@ -0,0 +1 @@ +build diff --git a/hilti/src/3rdparty/justrx/CMakeLists.txt b/hilti/src/3rdparty/justrx/CMakeLists.txt new file mode 100644 index 000000000..c0d0b8c53 --- /dev/null +++ b/hilti/src/3rdparty/justrx/CMakeLists.txt @@ -0,0 +1,43 @@ + +project(JustRX) +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +find_package(BISON REQUIRED) +find_package(FLEX REQUIRED) + +set(autogen "${CMAKE_CURRENT_BINARY_DIR}/justrx/autogen") + +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${autogen}) + +FLEX_TARGET(scanner src/re-scan.l ${autogen}/re-scan.c DEFINES_FILE ${autogen}/re-scan.h) +BISON_TARGET(parser src/re-parse.y ${autogen}/re-parse.c COMPILE_FLAGS "-Wno-conflicts-rr -Wno-conflicts-sr") +ADD_FLEX_BISON_DEPENDENCY(scanner parser) + +set(SOURCES + src/ccl.c + src/dfa.c + src/dfa-interpreter-std.c + src/dfa-interpreter-min.c + src/jlocale.c + src/jrx.c + src/nfa.c + src/util.c + + ${BISON_parser_OUTPUTS} + ${FLEX_scanner_OUTPUTS} + ) + +add_library(jrx ${SOURCES}) +# target_link_libraries(jrx ${FLEX_LIBRARIES}) +target_include_directories(jrx PUBLIC include) +target_include_directories(jrx PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +add_library(jrx-objects OBJECT ${SOURCES}) +target_compile_options(jrx-objects PRIVATE "-fPIC") +# target_link_libraries(jrx-objects ${FLEX_LIBRARIES}) +target_include_directories(jrx-objects PUBLIC include) +target_include_directories(jrx-objects PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory(test) diff --git a/hilti/src/3rdparty/justrx/COPYING b/hilti/src/3rdparty/justrx/COPYING new file mode 100644 index 000000000..3d4d270f2 --- /dev/null +++ b/hilti/src/3rdparty/justrx/COPYING @@ -0,0 +1,28 @@ +Copyright (c) 2014-2016, International Computer Science Institute. 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 name of the International Computer Science Institute, + nor the names of 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. diff --git a/hilti/src/3rdparty/justrx/Makefile b/hilti/src/3rdparty/justrx/Makefile new file mode 100644 index 000000000..ed51c95f6 --- /dev/null +++ b/hilti/src/3rdparty/justrx/Makefile @@ -0,0 +1,13 @@ +# $Id$ +# +# Build libhilti + +all: do-build + +do-build: + test -e build || mkdir build + cd build; test -e Makefile || cmake .. ; make + +clean: + rm -rf build + diff --git a/hilti/src/3rdparty/justrx/README b/hilti/src/3rdparty/justrx/README new file mode 100644 index 000000000..e5e0ec369 --- /dev/null +++ b/hilti/src/3rdparty/justrx/README @@ -0,0 +1,2 @@ + +TODO. diff --git a/hilti/src/3rdparty/justrx/TODO b/hilti/src/3rdparty/justrx/TODO new file mode 100644 index 000000000..67fe29db3 --- /dev/null +++ b/hilti/src/3rdparty/justrx/TODO @@ -0,0 +1,27 @@ + +- We don't optimize for the NO_CAPTURE case yet. If given, we can skip the + whole tagging stuff. + +- No locale support + * Extend jrx-local.{h,c}. + +- CCL optimization + * Precompute CCLs for codepoints < 256. + * CCLs should use a better data structure to represent sets of + intervals. + +- DFA optimizations: + * Remove duplicate states + * Join transitions to the same target state where CCLs are subset of each other. + * Join neighboring ranges in CCLs into a single range. + * After the DFA has been computed, some sets/vectors could be frozen. + * More generally, we could store the DFA states in a more compressed + format. + +- The groups returned for capturing expressions aren't always + POSIX-conform (i.e., not always what you would expect). + +- Just-in-time into LLVM code. + +- Add unicode matching. + diff --git a/hilti/src/3rdparty/justrx/bin/jrx-config.in b/hilti/src/3rdparty/justrx/bin/jrx-config.in new file mode 100755 index 000000000..5c9b7d748 --- /dev/null +++ b/hilti/src/3rdparty/justrx/bin/jrx-config.in @@ -0,0 +1,65 @@ +#! /usr/bin/env bash +# +# + +function usage { + cat < + + --cflags Print flags for compiling C sources. + --cxxflags Print flags for compiling C++ sources. + --distbase Print path of the source distribution. + --ldflags Print flags for linking. + --libs Print libaries for linking. + + --debug Print all output suitable for building debugging versions. + Note that this option must come first. + + --version Print version. + +EOF + exit 1 +} + +base=@CMAKE_INSTALL_PREFIX@ + +if [ "$#" == "0" ]; then + usage +fi + +debug="" + +while [ "$1" != "" ]; do + case $1 in + --version) + echo "@PROJECT_NAME@ @PROJECT_VERSION@" + shift;; + + --distbase) + echo $base + shift;; + + --ldflags) + echo "@CONFIG_LDFLAGS@" + shift;; + + --libs) + echo "@CONFIG_LIBS@" + shift;; + + --cflags) + echo "@CONFIG_CFLAGS@" + shift;; + + --cxxflags) + echo "@CONFIG_CXXFLAGS@" + shift;; + + *) + usage;; + esac +done + + + + diff --git a/hilti/src/3rdparty/justrx/cmake b/hilti/src/3rdparty/justrx/cmake new file mode 100644 index 000000000..e69de29bb diff --git a/hilti/src/3rdparty/justrx/include/justrx b/hilti/src/3rdparty/justrx/include/justrx new file mode 120000 index 000000000..5cd551cf2 --- /dev/null +++ b/hilti/src/3rdparty/justrx/include/justrx @@ -0,0 +1 @@ +../src \ No newline at end of file diff --git a/hilti/src/3rdparty/justrx/src/CMakeLists.txt b/hilti/src/3rdparty/justrx/src/CMakeLists.txt new file mode 100644 index 000000000..e69de29bb diff --git a/hilti/src/3rdparty/justrx/src/Makefile b/hilti/src/3rdparty/justrx/src/Makefile new file mode 100644 index 000000000..bbf87552e --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/Makefile @@ -0,0 +1,4 @@ +# $Id$ + +all: + ( cd .. && make ) diff --git a/hilti/src/3rdparty/justrx/src/ccl.c b/hilti/src/3rdparty/justrx/src/ccl.c new file mode 100644 index 000000000..6c2f0cc4b --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/ccl.c @@ -0,0 +1,496 @@ +// $Id$ + +#include "ccl.h" +#include "jlocale.h" +#include "jrx-intern.h" +#include "util.h" + +#include +#include + +static jrx_ccl* _ccl_create_epsilon() +{ + jrx_ccl* ccl = (jrx_ccl*)malloc(sizeof(jrx_ccl)); + ccl->id = 0; + ccl->group = 0; + ccl->assertions = 0; + ccl->ranges = 0; + return ccl; +} + +static jrx_ccl* _ccl_create_empty() +{ + jrx_ccl* ccl = _ccl_create_epsilon(); + ccl->ranges = set_char_range_create(0); + return ccl; +} + +// Not exposed; delete whole CCL group instead. +static void _ccl_delete(jrx_ccl* ccl) +{ + if ( ccl->group ) { + vec_ccl_set(ccl->group->ccls, ccl->id, 0); + ccl->group = 0; + } + + if ( ccl->ranges ) + set_char_range_delete(ccl->ranges); + + free(ccl); +} + +static int _ccl_is_part_of(jrx_ccl* ccl1, jrx_ccl* ccl2) +{ + if ( ccl1->assertions != ccl2->assertions ) + return 0; + + if ( ! ccl1->ranges ) + return 1; + + if ( ! ccl2->ranges ) + return 0; + + set_for_each(char_range, ccl1->ranges, r1) + { + int found = 0; + set_for_each(char_range, ccl2->ranges, r2) + { + if ( r1.begin >= r2.begin && r1.end <= r2.end ) + found = 1; + } + + if ( ! found ) + return 0; + } + + return 1; +} + +static int _ccl_compare(jrx_ccl* ccl1, jrx_ccl* ccl2) +{ + if ( ccl1->assertions != ccl2->assertions ) + return 0; + + int p1 = _ccl_is_part_of(ccl1, ccl2); + int p2 = _ccl_is_part_of(ccl2, ccl1); + + return p1 && p2; +} + +static jrx_ccl* _ccl_group_find(jrx_ccl_group* group, jrx_ccl* ccl) +{ + vec_for_each(ccl, group->ccls, gccl) + { + if ( gccl && _ccl_compare(ccl, gccl) ) + return gccl; + } + + return 0; +} + +static jrx_ccl* _ccl_group_add_to(jrx_ccl_group* group, jrx_ccl* ccl) +{ + jrx_ccl* existing = _ccl_group_find(group, ccl); + if ( existing ) { + if ( existing != ccl ) + _ccl_delete(ccl); + + assert(existing->group == group); + return existing; + } + + ccl->group = group; + ccl->id = vec_ccl_size(group->ccls); + vec_ccl_set(group->ccls, ccl->id, ccl); + return ccl; +} + +static jrx_ccl* _ccl_copy(jrx_ccl* ccl) +{ + jrx_ccl* copy = _ccl_create_epsilon(); + copy->assertions = ccl->assertions; + copy->ranges = ccl->ranges ? set_char_range_copy(ccl->ranges) : 0; + return copy; +} + +// Deletes empty ranges from CCL. +static void _ccl_cleanup(jrx_ccl* ccl) +{ + if ( ! ccl->ranges ) + return; + + set_char_range* nranges = set_char_range_create(0); + + // Keep only non-empty intervals. + set_for_each(char_range, ccl->ranges, r) + { + if ( r.begin < r.end ) + set_char_range_insert(nranges, r); + } + + set_char_range_delete(ccl->ranges); + ccl->ranges = nranges; +} + +static void _ccl_subtract_range(jrx_ccl* ccl, jrx_char_range sub) +{ + if ( ! ccl->ranges ) + return; + + set_char_range* addlranges = set_char_range_create(0); + + set_for_each(char_range, ccl->ranges, r) + { + if ( sub.begin >= r.begin && sub.begin <= r.end ) { + if ( sub.end >= r.begin && sub.end <= r.end ) { + // Need to split range. + jrx_char_range nr1 = {r.begin, sub.begin}; + set_char_range_insert(addlranges, nr1); + + jrx_char_range nr2 = {sub.end, r.end}; + set_char_range_insert(addlranges, nr2); + } + + else { + // Adjust right end. + jrx_char_range nr = {r.begin, sub.begin}; + set_char_range_insert(addlranges, nr); + } + } + + else if ( sub.end >= r.begin && sub.end <= r.end ) { + // Adjust left end. + jrx_char_range nr = {sub.end, r.end}; + set_char_range_insert(addlranges, nr); + } + + else if ( sub.begin <= r.begin && sub.end >= r.end ) { + jrx_char_range nr = {r.begin, r.begin}; + set_char_range_insert(addlranges, nr); + } + else + set_char_range_insert(addlranges, r); + } + + set_char_range_delete(ccl->ranges); + ccl->ranges = addlranges; +} + +static void _ccl_subtract(jrx_ccl* ccl1, jrx_ccl* ccl2) +{ + if ( ! ccl2->ranges ) + return; + + if ( ccl1->assertions != ccl2->assertions ) + return; + + set_for_each(char_range, ccl2->ranges, r) _ccl_subtract_range(ccl1, r); + + _ccl_cleanup(ccl1); +} + +static jrx_ccl* _ccl_intersect(jrx_ccl* ccl1, jrx_ccl* ccl2) +{ + if ( ! (ccl1->ranges && ccl2->ranges) ) + return 0; + + if ( ccl1->assertions != ccl2->assertions ) + return 0; + + set_char_range* nranges = set_char_range_create(0); + + set_for_each(char_range, ccl1->ranges, r1) + { + set_for_each(char_range, ccl2->ranges, r2) + { + if ( r2.begin >= r1.begin && r2.begin <= r1.end ) { + jrx_char_range nr = {r2.begin, (r1.end < r2.end ? r1.end : r2.end)}; + set_char_range_insert(nranges, nr); + } + + else if ( r2.end >= r1.begin && r2.end <= r1.end ) { + jrx_char_range nr = {r1.begin, r2.end}; + set_char_range_insert(nranges, nr); + } + + else if ( r1.begin >= r2.begin && r1.begin <= r2.end ) { + jrx_char_range nr = {r1.begin, (r2.end < r1.end ? r2.end : r1.end)}; + set_char_range_insert(nranges, nr); + } + + else if ( r1.end >= r2.begin && r1.end <= r2.end ) { + jrx_char_range nr = {r2.begin, r1.end}; + set_char_range_insert(nranges, nr); + } + } + } + + jrx_ccl* nccl = _ccl_create_epsilon(); + nccl->ranges = nranges; + nccl->assertions = ccl1->assertions; + + _ccl_cleanup(nccl); + + if ( ! ccl_is_empty(nccl) ) + return nccl; + + + _ccl_delete(nccl); + return 0; + +} + +jrx_ccl_group* ccl_group_create() +{ + jrx_ccl_group* group = (jrx_ccl_group*)malloc(sizeof(jrx_ccl_group)); + group->std_ccls = vec_std_ccl_create(0); + group->ccls = vec_ccl_create(0); + return group; +} + +void ccl_group_delete(jrx_ccl_group* group) +{ + vec_for_each(ccl, group->ccls, ccl) + { + if ( ccl ) + _ccl_delete(ccl); + } + + vec_ccl_delete(group->ccls); + vec_std_ccl_delete(group->std_ccls); + + free(group); +} + +void ccl_group_print(jrx_ccl_group* group, FILE* file) +{ + vec_for_each(ccl, group->ccls, ccl) + { + fputs(" ", file); + if ( ccl ) + ccl_print(ccl, file); + fputc('\n', file); + } +} + +jrx_ccl* ccl_empty(jrx_ccl_group* group) +{ + jrx_ccl* ccl = _ccl_create_empty(); + return _ccl_group_add_to(group, ccl); +} + +jrx_ccl* ccl_from_range(jrx_ccl_group* group, jrx_char begin, jrx_char end) +{ + jrx_ccl* ccl = _ccl_create_empty(); + jrx_char_range r = {begin, end}; + set_char_range_insert(ccl->ranges, r); + return _ccl_group_add_to(group, ccl); +} + +jrx_ccl* ccl_from_std_ccl(jrx_ccl_group* group, jrx_std_ccl std) +{ + jrx_ccl* ccl = vec_std_ccl_get(group->std_ccls, std); + + if ( ccl ) + return ccl; + + switch ( std ) { + case JRX_STD_CCL_EPSILON: + ccl = _ccl_create_epsilon(); + break; + + case JRX_STD_CCL_ANY: + ccl = ccl_from_range(group, 0, JRX_CHAR_MAX); + break; + + case JRX_STD_CCL_LOWER: + ccl = local_ccl_lower(group); + break; + + case JRX_STD_CCL_UPPER: + ccl = local_ccl_upper(group); + break; + + case JRX_STD_CCL_WORD: + ccl = local_ccl_word(group); + break; + + case JRX_STD_CCL_DIGIT: + ccl = local_ccl_digit(group); + break; + + case JRX_STD_CCL_BLANK: + ccl = local_ccl_blank(group); + break; + + case JRX_STD_CCL_NONE: + jrx_internal_error("ccl_from_std_ccl: JRX_STD_CCL_NONE given"); + + default: + jrx_internal_error("ccl_from_std_ccl: unknown std_ccl type"); + } + + assert(ccl); + + ccl = _ccl_group_add_to(group, ccl); + vec_std_ccl_set(group->std_ccls, std, ccl); + return ccl; +} + +extern jrx_ccl* ccl_any(jrx_ccl_group* group) +{ + return ccl_from_std_ccl(group, JRX_STD_CCL_ANY); +} + +extern jrx_ccl* ccl_epsilon(jrx_ccl_group* group) +{ + return ccl_from_std_ccl(group, JRX_STD_CCL_EPSILON); +} + +jrx_ccl* ccl_negate(jrx_ccl* ccl) +{ + assert(! ccl_is_epsilon(ccl)); + + jrx_ccl* copy = _ccl_create_empty(); + copy->assertions = ccl->assertions; + + if ( (! ccl->ranges) || set_char_range_size(ccl->ranges) == 0 ) { + // FIXME: technically, this should be RE_CHAR_MAX + 1... + jrx_char_range r = {0, JRX_CHAR_MAX}; + set_char_range_insert(copy->ranges, r); + return _ccl_group_add_to(ccl->group, ccl); + } + + jrx_char last = 0; + + set_for_each(char_range, ccl->ranges, r) + { + jrx_char_range nr = {last, r.begin}; + set_char_range_insert(copy->ranges, nr); + last = r.end; + } + + jrx_char_range final = {last, JRX_CHAR_MAX}; + set_char_range_insert(copy->ranges, final); + + _ccl_cleanup(copy); + + return _ccl_group_add_to(ccl->group, copy); +} + +jrx_ccl* ccl_add_assertions(jrx_ccl* ccl, jrx_assertion assertions) +{ + jrx_ccl* copy = _ccl_copy(ccl); + copy->assertions |= assertions; + return _ccl_group_add_to(ccl->group, copy); +} + +jrx_ccl* ccl_join(jrx_ccl* ccl1, jrx_ccl* ccl2) +{ + assert(ccl1->group == ccl2->group); + assert(ccl1->assertions == ccl2->assertions); + + // FIXME: we don't treat non-disjunct ranges correctly. + + jrx_ccl* ccl = _ccl_create_empty(); + if ( ccl1->ranges ) + set_char_range_join(ccl->ranges, ccl1->ranges); + + if ( ccl2->ranges ) + set_char_range_join(ccl->ranges, ccl2->ranges); + + return _ccl_group_add_to(ccl1->group, ccl); +} + +int ccl_is_empty(jrx_ccl* ccl) +{ + if ( ! ccl ) + return 1; + + return (! ccl->ranges) || set_char_range_size(ccl->ranges) == 0; +} + +int ccl_is_epsilon(jrx_ccl* ccl) +{ + if ( ! ccl ) + return 1; + + return ! ccl->ranges; +} + +jrx_ccl* ccl_group_add(jrx_ccl_group* group, jrx_ccl* ccl) +{ + return _ccl_group_add_to(group, _ccl_copy(ccl)); +} + +void ccl_group_disambiguate(jrx_ccl_group* group) +{ + int changed; + + do { + changed = 0; + + jrx_ccl_id i; + jrx_ccl_id j; + for ( i = 0; i < vec_ccl_size(group->ccls); i++ ) + for ( j = i + 1; j < vec_ccl_size(group->ccls); j++ ) { + jrx_ccl* ccl1 = vec_ccl_get(group->ccls, i); + jrx_ccl* ccl2 = vec_ccl_get(group->ccls, j); + + if ( ccl_is_epsilon(ccl1) || ccl_is_epsilon(ccl2) ) + continue; + + if ( ccl_is_empty(ccl1) || ccl_is_empty(ccl2) ) + continue; + + jrx_ccl* intersect = _ccl_intersect(ccl1, ccl2); + if ( ! intersect ) + continue; + + jrx_ccl* tmp_ccl1 = _ccl_copy(ccl1); + _ccl_subtract(ccl1, ccl2); + _ccl_subtract(ccl2, tmp_ccl1); + _ccl_delete(tmp_ccl1); + + _ccl_group_add_to(group, intersect); + + changed = 1; + } + + } while ( changed ); +} + +int ccl_do_intersect(jrx_ccl* ccl1, jrx_ccl* ccl2) +{ + if ( ! ccl1->ranges && ! ccl2->ranges ) + return 1; + + jrx_ccl* is = _ccl_intersect(ccl1, ccl2); + if ( is ) + _ccl_delete(is); + + return is != 0; +} + +void ccl_print(jrx_ccl* ccl, FILE* file) +{ + assert(ccl); + + fprintf(file, "#%d[", ccl->id); + + if ( ! ccl->ranges ) + fprintf(file, "Epsilon"); + else { + set_for_each(char_range, ccl->ranges, r) + { + fprintf(file, "(%d-", r.begin); + if ( r.end < JRX_CHAR_MAX ) + fprintf(file, "%d)", r.end); + else + fprintf(file, "max)"); + } + } + fputc(']', file); + + fprintf(file, " (assertions %d)", ccl->assertions); +} diff --git a/hilti/src/3rdparty/justrx/src/ccl.h b/hilti/src/3rdparty/justrx/src/ccl.h new file mode 100644 index 000000000..31a9931c3 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/ccl.h @@ -0,0 +1,74 @@ +// $Id$ +// +// TODO: We should really use better data structures for (a) managing sets of +// intervals within a CCL; and (b) managing set of CCLs. + +#ifndef JRX_CCL_H +#define JRX_CCL_H + +#include + +#include "jrx-intern.h" +#include "set.h" +#include "vector.h" + +// A range of characters. +typedef struct { + jrx_char begin; // First element of range. + jrx_char end; // One after last element of range. +} jrx_char_range; + +struct jrx_ccl; + +DECLARE_SET(ccl_id, jrx_ccl_id, uint32_t, SET_STD_EQUAL) +DECLARE_VECTOR(ccl, struct jrx_ccl*, jrx_ccl_id) +DECLARE_VECTOR(std_ccl, struct jrx_ccl*, jrx_std_ccl) + +// A collection of character classes, managed jointly. +typedef struct { + vec_ccl* ccls; // Vector of all CCLs indexed by their ID. + vec_std_ccl* std_ccls; // Cache for standard CCLs once computed. +} jrx_ccl_group; + +static inline int _jrx_cmp_char_ranges(jrx_char_range r1, jrx_char_range r2) +{ + return r1.begin != r2.begin ? SET_STD_EQUAL(r1.begin, r2.begin) : SET_STD_EQUAL(r1.end, r2.end); +} + +// A set of character ranges. +DECLARE_SET(char_range, jrx_char_range, uint32_t, _jrx_cmp_char_ranges) + +// A character class. +typedef struct jrx_ccl { + jrx_ccl_id id; // ID of CCL, unique within CCL group. + jrx_ccl_group* group; // The group this CCL is part of. + jrx_assertion assertions; // Assertions required for CCL to apply. + set_char_range* ranges; // Ranges for this CCL; NULL for epsilon transition. +} jrx_ccl; + +extern jrx_ccl* ccl_empty(jrx_ccl_group* group); +extern void ccl_print(jrx_ccl* ccl, FILE* file); + +// Do not modify any of the CCLs returned directly; use only the functions +// provided here for that. +extern jrx_ccl* ccl_from_range(jrx_ccl_group* group, jrx_char begin, jrx_char end); +extern jrx_ccl* ccl_from_std_ccl(jrx_ccl_group* group, jrx_std_ccl stdl); +extern jrx_ccl* ccl_epsilon(jrx_ccl_group* group); +extern jrx_ccl* ccl_any(jrx_ccl_group* group); + +extern jrx_ccl* ccl_negate(jrx_ccl* ccl); +extern jrx_ccl* ccl_add_assertions(jrx_ccl* ccl, jrx_assertion assertions); +extern jrx_ccl* ccl_join(jrx_ccl* ccl1, jrx_ccl* ccl2); + +extern int ccl_is_empty(jrx_ccl* ccl); +extern int ccl_is_epsilon(jrx_ccl* ccl); +extern int ccl_do_intersect(jrx_ccl* ccl1, jrx_ccl* ccl2); + +extern jrx_ccl_group* ccl_group_create(); +extern void ccl_group_delete(jrx_ccl_group* group); +extern void ccl_group_print(jrx_ccl_group* group, FILE* file); +extern jrx_ccl* ccl_group_add(jrx_ccl_group* group, jrx_ccl* ccl); + +extern void ccl_group_disambiguate(jrx_ccl_group* group); + +#endif diff --git a/hilti/src/3rdparty/justrx/src/dfa-interpreter-min.c b/hilti/src/3rdparty/justrx/src/dfa-interpreter-min.c new file mode 100644 index 000000000..cd567d2fc --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/dfa-interpreter-min.c @@ -0,0 +1,120 @@ +// $Id$ + +#include "dfa-interpreter-min.h" +#include "jlocale.h" +#include "jrx-intern.h" +#include "nfa.h" +#include "util.h" + +static int _ccl_match_assertions(jrx_char cp, jrx_char* previous, jrx_assertion have, + jrx_assertion want) +{ + if ( want & JRX_ASSERTION_WORD_BOUNDARY ) + have |= local_word_boundary(previous, cp) ? JRX_ASSERTION_WORD_BOUNDARY : 0; + + if ( want & JRX_ASSERTION_NOT_WORD_BOUNDARY ) + have |= local_word_boundary(previous, cp) ? 0 : JRX_ASSERTION_NOT_WORD_BOUNDARY; + + return (want & have) == want; +} + +static int _ccl_match(jrx_ccl* ccl, jrx_char cp, jrx_char* previous, jrx_assertion assertions) +{ + if ( ! ccl->ranges ) + return 0; + + if ( ! _ccl_match_assertions(cp, previous, assertions, ccl->assertions) ) + return 0; + + // Look at ranges. + set_for_each(char_range, ccl->ranges, r) + { + if ( cp >= r.begin && cp < r.end ) + return 1; + } + + return 0; +} + +int jrx_match_state_advance_min(jrx_match_state* ms, jrx_char cp, jrx_assertion assertions) +{ + jrx_dfa_state* state = dfa_get_state(ms->dfa, ms->state); + + if ( ! state ) + return 0; + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, "> in state #%d with input symbol %d and assertions %d ", ms->state, cp, + assertions); + + vec_for_each(dfa_transition, state->trans, trans) + { + jrx_ccl* ccl = vec_ccl_get(ms->dfa->ccls->ccls, trans.ccl); + + if ( ! _ccl_match(ccl, cp, ms->offset == 0 ? &ms->previous : 0, assertions) ) + // Doesn't match. + continue; + + ++ms->offset; + + // Found transition. + jrx_dfa_state_id succ_id = trans.succ; + jrx_dfa_state* succ_state = dfa_get_state(ms->dfa, succ_id); + + ms->state = succ_id; + ms->previous = cp; + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, "-> found transition, new state is #%d", succ_id); + + if ( succ_state->accepts ) { + jrx_accept_id aid = vec_dfa_accept_get(succ_state->accepts, 0).aid; + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, " (accepting with ID %d)\n", aid); + + // Accepting. + return aid; + } + + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fputs("\n", stderr); + + // Partial match. + return -1; + + } + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fputs("-> no transition possible", stderr); + + // Matching failed. Check if the start state is already an accepting one. + if ( state->accepts ) { + jrx_accept_id aid = vec_dfa_accept_get(state->accepts, 0).aid; + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, " (accepting with ID %d)\n", aid); + + ms->state = -1; // Jam it. + + // Accepting. + return aid; + } + + return 0; +} + +void jrx_match_state_copy(jrx_match_state* from, jrx_match_state* to) { + if ( from->cflags & REG_STD_MATCHER ) + jrx_internal_error("jrx_match_state_copy() used with state from standard matcher; that's not supported"); + + to->offset = from->offset; + to->begin = from->begin; + to->dfa = from->dfa; + to->state = from->state; + to->previous = from->previous; + to->cflags = from->cflags; + // Skip fields only used by the full matcher. + to->acc = from->acc; +} diff --git a/hilti/src/3rdparty/justrx/src/dfa-interpreter-min.h b/hilti/src/3rdparty/justrx/src/dfa-interpreter-min.h new file mode 100644 index 000000000..b92df7e8a --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/dfa-interpreter-min.h @@ -0,0 +1,19 @@ +// $Id$ +// +// Minimal matcher interface, for interpreting a compiled DFA without capture +// support. This interface is compatible to functions produced by the JIT +// compilation into LLVM code. + +#ifndef JRX_DFA_MIN_MATCHER_H +#define JRX_DFA_MIN_MATCHER_H + +#include "dfa.h" +#include "jrx-intern.h" + +// >0: Match with the return accept ID (if multiple match, undefined which). +// 0: Failure to match, not recoverable. +// -1: Partial match (e.g., no match yet but might still happen). +// *prev must be NULL initially and not modified between calls. +extern int jrx_match_state_advance_min(jrx_match_state* ms, jrx_char cp, jrx_assertion assertions); + +#endif diff --git a/hilti/src/3rdparty/justrx/src/dfa-interpreter-std.c b/hilti/src/3rdparty/justrx/src/dfa-interpreter-std.c new file mode 100644 index 000000000..f47904160 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/dfa-interpreter-std.c @@ -0,0 +1,302 @@ +// $Id$ + +#include "dfa-interpreter-std.h" +#include "jlocale.h" +#include "jrx-intern.h" +#include "nfa.h" + +static int _ccl_match_assertions(jrx_char cp, jrx_char* previous, jrx_assertion have, + jrx_assertion want) +{ + if ( want & JRX_ASSERTION_WORD_BOUNDARY ) + have |= local_word_boundary(previous, cp) ? JRX_ASSERTION_WORD_BOUNDARY : 0; + + if ( want & JRX_ASSERTION_NOT_WORD_BOUNDARY ) + have |= local_word_boundary(previous, cp) ? 0 : JRX_ASSERTION_NOT_WORD_BOUNDARY; + + return (want & have) == want; +} + +static int _ccl_match(jrx_ccl* ccl, jrx_char cp, jrx_char* previous, jrx_assertion assertions) +{ + if ( ! ccl->ranges ) + return 0; + + if ( ! _ccl_match_assertions(cp, previous, assertions, ccl->assertions) ) + return 0; + + // Look at ranges. + set_for_each(char_range, ccl->ranges, r) + { + if ( cp >= r.begin && cp < r.end ) + return 1; + } + + return 0; +} + +static inline size_t _tag_group_size(jrx_match_state* ms) +{ + return (ms->dfa->max_tag + 1) * sizeof(jrx_offset); +} + +static inline void* _resize_tags(jrx_match_state* ms, jrx_offset* tags, int* size, + jrx_tag_group_id group) +{ + int old_size = *size * _tag_group_size(ms); + int new_size = (group + 1) * _tag_group_size(ms); + + char* t = realloc(tags, new_size); + memset(t + old_size, 0, new_size - old_size); + + *size = (group + 1); + return t; +} + +static inline jrx_offset* _tag_group(jrx_match_state* ms, int tags, jrx_tag_group_id group) +{ + jrx_offset* buffer; + + if ( tags ) { + if ( group >= ms->tags1_size ) + ms->tags1 = _resize_tags(ms, ms->tags1, &ms->tags1_size, group); + + buffer = ms->tags1; + } + + else { + if ( group >= ms->tags2_size ) + ms->tags2 = _resize_tags(ms, ms->tags2, &ms->tags2_size, group); + + buffer = ms->tags2; + } + + return (jrx_offset*)(((char*)buffer) + (group * _tag_group_size(ms))); +} + +static void _update_tags(jrx_match_state* ms, vec_tag_op* tops) +{ + if ( ! tops ) + return; + + int oldct = ms->current_tags; + int newct = 1 - oldct; + + // We first copy each old group to the new place. + vec_for_each(tag_op, tops, top) memcpy(_tag_group(ms, newct, top.tnew), + _tag_group(ms, oldct, top.told), _tag_group_size(ms)); + + // Now apply operations. + vec_for_each(tag_op, tops, top2) + { + if ( top2.tag < 0 ) + continue; + + jrx_offset* group = _tag_group(ms, newct, top2.tnew); + group[top2.tag] = ms->offset; + } + + ms->current_tags = newct; +} + +static void _update_accepts(jrx_match_state* ms, jrx_dfa_state* state, jrx_char cp, + jrx_assertion assertions) +{ + if ( ! state->accepts ) + return; + + vec_for_each(dfa_accept, state->accepts, acc) + { + if ( ! _ccl_match_assertions(cp, (ms->offset ? &ms->previous : 0), assertions, + acc.final_assertions) ) + // No match, final assertions don't work out. + continue; + + if ( ms->dfa->options & JRX_OPTION_NO_CAPTURE ) { + jrx_match_accept nacc = {acc.aid, 0}; + set_match_accept_insert(ms->accepts, nacc); + return; + } + + jrx_offset* tags = jrx_match_state_copy_tags(ms, acc.tid); + + if ( acc.final_ops ) { + vec_for_each(tag_op, acc.final_ops, op) + { + tags[op.tag] = ms->offset; + } + } + + jrx_match_accept nacc = {acc.aid, tags}; + + // If we already have an entry with that acc.aid, we only keep the + // one with the left-most longest match. + + int found_old = 0; + set_for_each(match_accept, ms->accepts, oacc) + { + if ( nacc.aid != oacc.aid ) + continue; + + found_old = 1; + + int olen = oacc.tags[0] > 0 && oacc.tags[1] > 0 ? oacc.tags[1] - oacc.tags[0] : -1; + int nlen = nacc.tags[0] > 0 && nacc.tags[1] > 0 ? nacc.tags[1] - nacc.tags[0] : -1; + + if ( nlen < 0 ) + goto keep_old; + + if ( olen < 0 ) + goto keep_new; + + if ( oacc.tags[0] < nacc.tags[0] ) + // Left-most. + goto keep_old; + + if ( oacc.tags[0] == nacc.tags[0] && nlen > olen ) + // Longest if same start position. + goto keep_new; + + keep_old: + free(nacc.tags); + break; + + keep_new: + if ( oacc.tags ) + free(oacc.tags); + + set_match_accept_remove(ms->accepts, oacc); + set_match_accept_insert(ms->accepts, nacc); + break; + } + + if ( ! found_old ) + set_match_accept_insert(ms->accepts, nacc); + } +} + +jrx_match_state* jrx_match_state_init(const jrx_regex_t* preg, jrx_offset begin, + jrx_match_state* ms) +{ + jrx_dfa* dfa = preg->dfa; + + ms->offset = 1; + ms->begin = begin; + ms->previous = 0; + ms->dfa = dfa; + ms->state = dfa->initial; + ms->current_tags = 0; + ms->acc = -1; + ms->tags1 = 0; + ms->tags2 = 0; + ms->tags1_size = 0; + ms->tags2_size = 0; + ms->cflags = preg->cflags; + + if ( (dfa->options & JRX_OPTION_STD_MATCHER) ) { + ms->accepts = set_match_accept_create(0); + + _update_tags(ms, dfa->initial_ops); + jrx_dfa_state* state = dfa_get_state(ms->dfa, ms->state); + _update_accepts(ms, state, 0, JRX_ASSERTION_BOL | JRX_ASSERTION_BOD); + } + else { + ms->accepts = 0; + ms->current_tags = -1; + } + + return ms; +} + +void jrx_match_state_done(jrx_match_state* ms) +{ + if ( ms->dfa->options & JRX_OPTION_NO_CAPTURE ) + return; + + set_for_each(match_accept, ms->accepts, acc) + { + if ( acc.tags ) + free(acc.tags); + } + + set_match_accept_delete(ms->accepts); + + free(ms->tags1); + free(ms->tags2); +} + +static void print_accept_set(set_match_accept* s) +{ + fputs(" (accept set is [", stderr); + + int first = 1; + set_for_each(match_accept, s, acc) + { + if ( ! first ) + fputc(',', stderr); + fprintf(stderr, "(%d, 0x%p)", acc.aid, acc.tags); + first = 0; + } + + fputs("])\n", stderr); +} + +int jrx_match_state_advance(jrx_match_state* ms, jrx_char cp, jrx_assertion assertions) +{ + jrx_dfa_state* state = dfa_get_state(ms->dfa, ms->state); + + if ( ! state ) + return 0; + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, "> in state #%d at offset %d with input symbol %d and assertions %d ", + ms->state, ms->offset, cp, assertions); + + vec_for_each(dfa_transition, state->trans, trans) + { + jrx_ccl* ccl = vec_ccl_get(ms->dfa->ccls->ccls, trans.ccl); + + if ( ! _ccl_match(ccl, cp, ms->offset ? &ms->previous : 0, assertions) ) + // Doesn't match. + continue; + + // Found transition. + jrx_dfa_state_id succ_id = trans.succ; + jrx_dfa_state* succ_state = dfa_get_state(ms->dfa, succ_id); + vec_tag_op* tops = trans.tops; + + ms->state = succ_id; + ms->previous = cp; + + _update_tags(ms, tops); + + ++ms->offset; + + _update_accepts(ms, succ_state, cp, assertions); + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) { + fprintf(stderr, "-> found transition, new state is #%d", ms->state); + print_accept_set(ms->accepts); + } + + return 1; + } + + if ( ms->dfa->options & JRX_OPTION_DEBUG ) { + fprintf(stderr, "-> no transition possible"); + print_accept_set(ms->accepts); + } + + return 0; +} + +jrx_offset* jrx_match_state_copy_tags(jrx_match_state* ms, jrx_tag_group_id tid) +{ + jrx_offset* tags = (jrx_offset*)malloc(_tag_group_size(ms)); + jrx_offset* group = _tag_group(ms, ms->current_tags, tid); + memcpy(tags, group, _tag_group_size(ms)); + + assert(ms->current_tags ? ms->tags1 : ms->tags2); + + return tags; +} diff --git a/hilti/src/3rdparty/justrx/src/dfa-interpreter-std.h b/hilti/src/3rdparty/justrx/src/dfa-interpreter-std.h new file mode 100644 index 000000000..97e6796f7 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/dfa-interpreter-std.h @@ -0,0 +1,28 @@ +// $Id$ +// +// Matcher interface, for interpreting a compiled DFA. + +#ifndef JRX_DFA_MATCHER_H +#define JRX_DFA_MATCHER_H + +#include "dfa.h" +#include "jrx-intern.h" + +typedef struct { + jrx_accept_id aid; + jrx_offset* tags; +} jrx_match_accept; + + +static inline int _jrx_cmp_match_accept(jrx_match_accept a, jrx_match_accept b) +{ + return a.aid != b.aid ? SET_STD_EQUAL(a.aid, b.aid) : + SET_STD_EQUAL(a.tags, b.tags); // ptr comparision ok. +} + +DECLARE_SET(match_accept, jrx_match_accept, uint32_t, _jrx_cmp_match_accept) + +extern int jrx_match_state_advance(jrx_match_state* ms, jrx_char cp, jrx_assertion assertions); +extern jrx_offset* jrx_match_state_copy_tags(jrx_match_state* ms, jrx_tag_group_id tid); + +#endif diff --git a/hilti/src/3rdparty/justrx/src/dfa.c b/hilti/src/3rdparty/justrx/src/dfa.c new file mode 100644 index 000000000..75da6bfd7 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/dfa.c @@ -0,0 +1,524 @@ +// $Id$ + +#include "dfa.h" +#include "jrx-intern.h" + +static jrx_dfa* _dfa_create() +{ + jrx_dfa* dfa = (jrx_dfa*)malloc(sizeof(jrx_dfa)); + if ( ! dfa ) + return 0; + + dfa->options = 0; + dfa->nmatch = 0; + dfa->initial = 0; + dfa->initial_dstate = 0; + dfa->states = vec_dfa_state_create(0); + dfa->state_elems = vec_dfa_state_elem_create(0); + dfa->hstates = kh_init(dfa_state_elem); + dfa->ccls = 0; + dfa->max_capture = -1; + dfa->max_tag = -1; + dfa->nfa = 0; + + return dfa; +} + +static jrx_dfa_state* _dfa_state_create() +{ + jrx_dfa_state* dstate = (jrx_dfa_state*)malloc(sizeof(jrx_dfa_state)); + if ( ! dstate ) + return 0; + + dstate->accepts = 0; + dstate->trans = vec_dfa_transition_create(0); + return dstate; +} + +static void _dfa_state_delete(jrx_dfa_state* state) +{ + vec_for_each(dfa_transition, state->trans, trans) + { + if ( trans.tops ) + vec_tag_op_delete(trans.tops); + } + + vec_dfa_transition_delete(state->trans); + + if ( state->accepts ) { + vec_for_each(dfa_accept, state->accepts, acc) + { + if ( acc.final_ops ) + vec_tag_op_delete(acc.final_ops); + + if ( acc.tags ) + free(acc.tags); + } + + vec_dfa_accept_delete(state->accepts); + } + + free(state); +} + +static jrx_dfa_state_id reserve_dfastate_id(jrx_dfa* dfa, set_dfa_state_elem* dstate) +{ + jrx_dfa_state_id id = vec_dfa_state_append(dfa->states, 0); + vec_dfa_state_elem_append(dfa->state_elems, 0); + + assert(dstate); + + int ret; + khiter_t k = kh_put(dfa_state_elem, dfa->hstates, *dstate, &ret); + // I'm not quite sure what ret==0 signals but kh_del() seems to be be the + // idiom to use in that case ... + if ( ! ret ) + kh_del(dfa_state_elem, dfa->hstates, k); + + kh_value(dfa->hstates, k) = id; + return id; +} + +// This is similar to tag_op but also carries the NFA state ID for later +// disambiguation. +typedef struct { + jrx_nfa_state_id nid; + jrx_tag_group_id told; + jrx_tag_group_id tnew; + jrx_tag tag; +} _nid_tag_op; + +DECLARE_VECTOR(nid_tag_op, _nid_tag_op, uint32_t) + +static set_dfa_state_elem* transition_with(jrx_nfa_context* ctx, jrx_dfa* dfa, + set_dfa_state_elem* dstate, jrx_ccl* ccl, + vec_tag_op** tops) +{ + set_nfa_state_id* nstates = set_nfa_state_id_create(0); + vec_nid_tag_op* ntops = vec_nid_tag_op_create(0); + + jrx_tag_group_id tid = 0; + + // Get all our options with this CCL. + + set_for_each(dfa_state_elem, dstate, delem) + { + jrx_nfa_state* nstate = vec_nfa_state_get(ctx->states, delem.nid); + + vec_for_each(nfa_transition, nstate->trans, trans) + { + jrx_ccl* nccl = vec_ccl_get(ctx->ccls->ccls, trans.ccl); + if ( ccl_do_intersect(nccl, ccl) ) { + set_nfa_state_id_insert(nstates, trans.succ); + + ++tid; + + if ( trans.tags ) { + // Make sure we get at least one entry for this state. + assert(set_tag_size(trans.tags)); + + set_for_each(tag, trans.tags, tag) + { + _nid_tag_op ntop = {trans.succ, delem.tid, tid, tag}; + vec_nid_tag_op_append(ntops, ntop); + } + } + + else { + // Just record a copy operation. + _nid_tag_op ntop = {trans.succ, delem.tid, tid, {-1, 0}}; + vec_nid_tag_op_append(ntops, ntop); + } + } + } + } + + // Now pick the right tag operations, using tag priorities to + // disambiguate if necessary. + + set_dfa_state_elem* ndstate = set_dfa_state_elem_create(0); + + set_for_each(nfa_state_id, nstates, nid) + { + // Determine tag set with highest priority for this state. + int8_t max_tprio = -127; + jrx_tag_group_id max_tnew = 0; + + vec_for_each(nid_tag_op, ntops, ntop) + { + if ( ntop.nid == nid && ntop.tag.prio >= max_tprio ) { + max_tprio = ntop.tag.prio; + max_tnew = ntop.tnew; + } + } + + // Add those entries with the highest priority. + vec_for_each(nid_tag_op, ntops, ntop2) + { + if ( ntop2.nid != nid ) + continue; + + if ( ntop2.tnew == max_tnew ) { + dfa_state_elem delem = {nid, ntop2.tnew}; + set_dfa_state_elem_insert(ndstate, delem); + + if ( ! *tops ) + *tops = vec_tag_op_create(0); + + jrx_tag_op nntop = {ntop2.told, ntop2.tnew, ntop2.tag.reg}; + vec_tag_op_append(*tops, nntop); + } + } + } + + set_nfa_state_id_delete(nstates); + vec_nid_tag_op_delete(ntops); + + return ndstate; +} + +static jrx_dfa_state sentinel; // Value is irrelevant. + +int dfa_state_compute(jrx_nfa_context* ctx, jrx_dfa* dfa, jrx_dfa_state_id id, + set_dfa_state_elem* dstate, int recurse) +{ + if ( vec_dfa_state_get(dfa->states, id) ) + // Already computed (or being worked on at the moment). + return 1; + + // We set the pointer for the state we are currently computing to a dummy + // value as an indicator that we're already working on it, to abort + // recursion when we get to it again. + vec_dfa_state_set(dfa->states, id, &sentinel); + // vec_dfa_state_elem_set(dfa->state_elems, id, 0); + + // Determine the transitions for all CCLs. + // + // FIXME: I guess we could precompute the relevant CCLs here, instead of + // trying all of them each time. + vec_dfa_transition* transitions = vec_dfa_transition_create(0); + + vec_for_each(ccl, dfa->ccls->ccls, ccl) + { + if ( ccl_is_empty(ccl) ) + continue; + + // Determine which states we can reach with this CCL, including the + // necessary tag operations to get there. + vec_tag_op* tops = 0; + set_dfa_state_elem* succ_dstate = transition_with(ctx, dfa, dstate, ccl, &tops); + + if ( set_dfa_state_elem_size(succ_dstate) == 0 ) { + // Well, none. + if ( tops ) + vec_tag_op_delete(tops); + + set_dfa_state_elem_delete(succ_dstate); + continue; + } + + // Do we already know that set of DFA states? + jrx_dfa_state_id succ_id; + int8_t old = 0; + + khiter_t k = kh_get(dfa_state_elem, dfa->hstates, *succ_dstate); + if ( k != kh_end(dfa->hstates) ) { + // Yes, use the old one. + set_dfa_state_elem_delete(succ_dstate); + succ_id = kh_value(dfa->hstates, k); + succ_dstate = &kh_key(dfa->hstates, k); + old = 1; + } + else + // No, get a new ID for it. + succ_id = reserve_dfastate_id(dfa, succ_dstate); + + // Record a transition for this CCL. + jrx_dfa_transition trans = {ccl->id, succ_id, tops}; + vec_dfa_transition_append(transitions, trans); + + // Recurse if we are asked to do so. + if ( recurse ) { + dfa_state_compute(ctx, dfa, succ_id, succ_dstate, 1); + // set_dfa_state_elem_delete(succ_dstate); + } + + else { + if ( ! old ) + // Records state for lazy computation. + vec_dfa_state_elem_set(dfa->state_elems, succ_id, succ_dstate); + } + } + + // Now build the DFA state. + jrx_dfa_state* dfastate = _dfa_state_create(); + + if ( dfastate->trans ) + vec_dfa_transition_delete(dfastate->trans); + + dfastate->trans = transitions; + + // Add accepts. + vec_dfa_accept* accepts = 0; + + set_for_each(dfa_state_elem, dstate, delem) + { + jrx_nfa_state* nstate = vec_nfa_state_get(ctx->states, delem.nid); + + if ( ! nstate->accepts ) + continue; + + vec_for_each(nfa_accept, nstate->accepts, acc) + { + vec_tag_op* tops = 0; + + if ( acc.tags ) { + tops = vec_tag_op_create(0); + set_for_each(tag, acc.tags, tag) + { + jrx_tag_op top = {delem.tid, delem.tid, tag.reg}; + vec_tag_op_append(tops, top); + } + } + + if ( ! accepts ) + accepts = vec_dfa_accept_create(0); + + jrx_dfa_accept dacc = {acc.assertions, acc.aid, delem.tid, tops, 0}; + vec_dfa_accept_append(accepts, dacc); + } + } + + dfastate->accepts = accepts; + + vec_dfa_state_set(dfa->states, id, dfastate); + return 1; +} + +jrx_dfa_state* dfa_get_state(jrx_dfa* dfa, jrx_dfa_state_id id) +{ + jrx_dfa_state* state = vec_dfa_state_get(dfa->states, id); + + if ( state ) + return state; + + set_dfa_state_elem* dstate = vec_dfa_state_elem_get(dfa->state_elems, id); + assert(dstate); + + dfa_state_compute(dfa->nfa->ctx, dfa, id, dstate, 0); + + state = vec_dfa_state_get(dfa->states, id); + assert(state); + return state; +} + +jrx_dfa* dfa_from_nfa(jrx_nfa* nfa) +{ + jrx_dfa* dfa = _dfa_create(); + if ( ! dfa ) + return 0; + + jrx_nfa_context* ctx = nfa->ctx; + + dfa->options = ctx->options; + dfa->nmatch = ctx->nmatch; + dfa->max_capture = ctx->max_capture; + dfa->max_tag = ctx->max_tag; + dfa->nfa = nfa; + + // Get us all the CCLs which are used in the NFA. + dfa->ccls = ccl_group_create(); + + vec_for_each(nfa_state, ctx->states, nstate) + { + vec_for_each(nfa_transition, nstate->trans, trans) + { + jrx_ccl* ccl = vec_ccl_get(ctx->ccls->ccls, trans.ccl); + if ( ! (ccl_is_empty(ccl) || ccl_is_epsilon(ccl)) ) + ccl_group_add(dfa->ccls, ccl); + } + } + + // Make them disjunct. + ccl_group_disambiguate(dfa->ccls); + + // Create the initial state. + set_dfa_state_elem* initial = set_dfa_state_elem_create(0); + dfa_state_elem ielem = {nfa->initial->id, 0}; + set_dfa_state_elem_insert(initial, ielem); + dfa->initial = reserve_dfastate_id(dfa, initial); + dfa->initial_dstate = initial; + + vec_tag_op* tops = 0; + + if ( nfa->initial_tags ) { + set_for_each(tag, nfa->initial_tags, tag) + { + if ( ! tops ) + tops = vec_tag_op_create(0); + + jrx_tag_op top = {0, 0, tag.reg}; + vec_tag_op_append(tops, top); + } + } + + dfa->initial_ops = tops; + + int lazy = (ctx->options & JRX_OPTION_LAZY); + dfa_state_compute(ctx, dfa, dfa->initial, initial, ! lazy); + + if ( ctx->options & JRX_OPTION_DEBUG ) + dfa_print(dfa, stderr); + + return dfa; +} + +void dfa_delete(jrx_dfa* dfa) +{ + if ( dfa->initial_ops ) + vec_tag_op_delete(dfa->initial_ops); + + vec_for_each(dfa_state, dfa->states, dstate) + { + if ( dstate ) + _dfa_state_delete(dstate); + } + + vec_for_each(dfa_state_elem, dfa->state_elems, state_elem) + { + if ( state_elem ) + set_dfa_state_elem_delete(state_elem); + } + + vec_dfa_state_elem_delete(dfa->state_elems); + + vec_dfa_state_delete(dfa->states); + kh_destroy(dfa_state_elem, dfa->hstates); + ccl_group_delete(dfa->ccls); + + if ( dfa->initial_dstate ) + set_dfa_state_elem_delete(dfa->initial_dstate); + + free(dfa); +} + +jrx_dfa* dfa_compile(const char* pattern, int len, jrx_option options, int8_t nmatch, + const char** errmsg) +{ + jrx_nfa* nfa = nfa_compile(pattern, len, options, nmatch, errmsg); + if ( ! nfa ) + return 0; + + jrx_dfa* dfa = dfa_from_nfa(nfa); + assert(dfa); + + if ( options & JRX_OPTION_DEBUG ) + dfa_print(dfa, stderr); + + return dfa; +} + +static void _vec_tag_op_print(vec_tag_op* tops, FILE* file) +{ + if ( ! tops ) { + fputs("none", file); + return; + } + + int first = 1; + vec_for_each(tag_op, tops, top) + { + if ( ! first ) + fputs(", ", file); + fprintf(file, "old=%d/new=%d/tag=%d", top.told, top.tnew, top.tag); + first = 0; + } +} + +static void _dfa_state_print(jrx_dfa* dfa, jrx_dfa_state* dstate, FILE* file) +{ + if ( ! dstate ) { + fputs("(not computed)", file); + return; + } + + if ( dstate->accepts ) { + fputs(" accepts with", file); + vec_for_each(dfa_accept, dstate->accepts, acc) + { + fprintf(file, " (%d, t%d, final assertions %d, final ops", acc.aid, acc.tid, + acc.final_assertions); + _vec_tag_op_print(acc.final_ops, file); + fprintf(stderr, ")\n"); + } + + fputs("\n", file); + } + + vec_for_each(dfa_transition, dstate->trans, trans) + { + fputs(" ", file); + jrx_ccl* ccl = vec_ccl_get(dfa->ccls->ccls, trans.ccl); + ccl_print(ccl, file); + fprintf(file, " -> %d", trans.succ); + + fprintf(file, " (tag ops are "); + _vec_tag_op_print(trans.tops, file); + fprintf(file, ")"); + fprintf(file, "\n"); + } +} + +void dfa_print(jrx_dfa* dfa, FILE* file) +{ + fprintf(file, "== DFA with %d states\n", vec_dfa_state_size(dfa->states)); + + fprintf(file, "options %d\n", dfa->options); + fprintf(file, "max tag %d\n", dfa->max_tag); + fprintf(file, "max capture %d\n", dfa->max_capture); + + fprintf(file, "initial tag ops are "); + _vec_tag_op_print(dfa->initial_ops, file); + fprintf(file, "\n"); + + vec_for_each(dfa_state, dfa->states, dstate) + { + fprintf(file, "state %d\n", __jdstate); + _dfa_state_print(dfa, dstate, file); + + if ( __jdstate == dfa->initial ) + fputs(" -> initial state\n", file); + + fputs("\n", file); + } + + if ( ! dfa->hstates ) { + fprintf(file, "(no state sets available)\n"); + return; + } + + fputs("state sets\n", file); + + khiter_t k; + for ( k = kh_begin(dfa->hstates); k != kh_end(dfa->hstates); ++k ) { + if ( ! kh_exist(dfa->hstates, k) ) + continue; + + fputs(" ( ", file); + + set_dfa_state_elem dstate = kh_key(dfa->hstates, k); + + set_for_each(dfa_state_elem, &dstate, delem) + fprintf(file, "(#%d, t%d) ", delem.nid, delem.tid); + + fputs(")", file); + + fprintf(file, " -> #%d\n", kh_value(dfa->hstates, k)); + } + + fputs("\n", file); + + fputs("CCLs:\n", file); + ccl_group_print(dfa->ccls, file); + fputs("\n", file); +} diff --git a/hilti/src/3rdparty/justrx/src/dfa.h b/hilti/src/3rdparty/justrx/src/dfa.h new file mode 100644 index 000000000..f4bee8b06 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/dfa.h @@ -0,0 +1,104 @@ +// $Id$ + +#ifndef JRX_DFA_H +#define JRX_DFA_H + +#include "jrx-intern.h" +#include "khash.h" +#include "nfa.h" +#include "set.h" + +typedef uint16_t jrx_jrx_tag_group_id; + +typedef struct { + jrx_nfa_state_id nid; // The NFA state. + jrx_jrx_tag_group_id tid; // The tag group we're storing tags in. +} dfa_state_elem; + +static inline int _jrx_cmp_dfa_state_elem(dfa_state_elem a, dfa_state_elem b) +{ + return a.nid != b.nid ? SET_STD_EQUAL(a.nid, b.nid) : SET_STD_EQUAL(a.tid, b.tid); +} + +DECLARE_SET(dfa_state_elem, dfa_state_elem, uint32_t, _jrx_cmp_dfa_state_elem) + +static inline khint_t _jrx_hash_dfa_state_elem(set_dfa_state_elem dstate) +{ + khint_t hash = set_dfa_state_elem_size(&dstate); + set_for_each(dfa_state_elem, &dstate, delem) hash = + (((hash << 4) ^ (hash >> 28)) + (delem.nid + delem.tid)); + + return hash; +} + +static inline khint_t _jrx_eq_set_dfa_state_elem(set_dfa_state_elem a, set_dfa_state_elem b) +{ + return set_dfa_state_elem_equal(&a, &b); +} + +KHASH_INIT(dfa_state_elem, set_dfa_state_elem, jrx_dfa_state_id, 1, _jrx_hash_dfa_state_elem, + _jrx_eq_set_dfa_state_elem) +typedef khash_t(dfa_state_elem) hash_dfa_state; + +typedef uint8_t jrx_tag_group_id; + +typedef struct { + jrx_tag_group_id told; + jrx_tag_group_id tnew; + int8_t tag; +} jrx_tag_op; + +DECLARE_VECTOR(tag_op, jrx_tag_op, uint32_t) + +typedef struct { + jrx_ccl_id ccl; + jrx_dfa_state_id succ; + vec_tag_op* tops; +} jrx_dfa_transition; + +DECLARE_VECTOR(dfa_transition, jrx_dfa_transition, uint32_t) + +typedef struct { + jrx_assertion final_assertions; // Final assertions required for accepting. + jrx_accept_id aid; // The ID to accept with. + jrx_tag_group_id tid; // The tag group to use. + vec_tag_op* final_ops; // Final tag operations when accepting. + jrx_offset* tags; // A copy of the final tag values. +} jrx_dfa_accept; + +DECLARE_VECTOR(dfa_accept, jrx_dfa_accept, uint32_t) + +typedef struct { + vec_dfa_accept* accepts; // Accepts for this state. + vec_dfa_transition* trans; // Transitions out of this state. +} jrx_dfa_state; + +DECLARE_VECTOR(dfa_state, jrx_dfa_state*, jrx_dfa_state_id) +DECLARE_VECTOR(dfa_state_elem, set_dfa_state_elem*, jrx_dfa_state_id) + +typedef struct jrx_dfa { + jrx_option options; // Options specified for compilation. + int8_t nmatch; // Max. number of captures the user is interested in. + int8_t max_tag; // Largest tag number used. + int8_t max_capture; // Largest capture group number used. + jrx_dfa_state_id initial; // Initial state. + set_dfa_state_elem* initial_dstate; // Initial state. + vec_tag_op* initial_ops; // Initial tag operations. + vec_dfa_state* states; // Array of DFA states, indexed by their ID. + vec_dfa_state_elem* state_elems; // Array of states, indexed by their ID. + hash_dfa_state* hstates; // Hash of states indexed by set of NFA states. + jrx_ccl_group* ccls; // CCLs for the DFA. + jrx_nfa* nfa; // The underlying NFA. +} jrx_dfa; + + +extern jrx_dfa* dfa_compile(const char* pattern, int len, jrx_option options, int8_t nmatch, + const char** errmsg); +extern jrx_dfa* dfa_from_nfa(jrx_nfa* nfa); +extern int dfa_state_compute(jrx_nfa_context* ctx, jrx_dfa* dfa, jrx_dfa_state_id id, + set_dfa_state_elem* dstate, int recurse); +extern jrx_dfa_state* dfa_get_state(jrx_dfa* dfa, jrx_dfa_state_id id); +extern void dfa_delete(jrx_dfa* dfa); +extern void dfa_print(jrx_dfa* dfa, FILE* file); + +#endif diff --git a/hilti/src/3rdparty/justrx/src/jlocale.c b/hilti/src/3rdparty/justrx/src/jlocale.c new file mode 100644 index 000000000..3a47d4d08 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/jlocale.c @@ -0,0 +1,49 @@ +// $Id$ + +#include "jlocale.h" +#include "jrx-intern.h" + +static inline jrx_ccl* _add_range(jrx_ccl* ccl, jrx_char min, jrx_char max) +{ + jrx_ccl* nccl = ccl_from_range(ccl->group, min, max + 1); + return ccl_join(ccl, nccl); +} + +jrx_ccl* local_ccl_lower(jrx_ccl_group* group) +{ + jrx_ccl* ccl = ccl_empty(group); + ccl = _add_range(ccl, 'a', 'z'); + return ccl; +} + +jrx_ccl* local_ccl_upper(jrx_ccl_group* group) +{ + jrx_ccl* ccl = ccl_empty(group); + ccl = _add_range(ccl, 'A', 'Z'); + return ccl; +} + +jrx_ccl* local_ccl_word(jrx_ccl_group* group) +{ + jrx_ccl* ccl = ccl_empty(group); + ccl = _add_range(ccl, 'a', 'z'); + ccl = _add_range(ccl, 'A', 'Z'); + ccl = _add_range(ccl, '0', '9'); + ccl = _add_range(ccl, '_', '_'); + return ccl; +} + +jrx_ccl* local_ccl_digit(jrx_ccl_group* group) +{ + jrx_ccl* ccl = ccl_empty(group); + ccl = _add_range(ccl, '0', '9'); + return ccl; +} + +jrx_ccl* local_ccl_blank(jrx_ccl_group* group) +{ + jrx_ccl* ccl = ccl_empty(group); + ccl = _add_range(ccl, ' ', ' '); + ccl = _add_range(ccl, '\t', '\t'); + return ccl; +} diff --git a/hilti/src/3rdparty/justrx/src/jlocale.h b/hilti/src/3rdparty/justrx/src/jlocale.h new file mode 100644 index 000000000..8f1db2c81 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/jlocale.h @@ -0,0 +1,32 @@ +// $Id$ +// +// Function implementing local- and encoding-specific functionality. +// +// TODO: Currently, these are just hard-coded in local-independent, +// ASCII-only way. + +#ifndef JRX_JITTYPE_H +#define JRX_JITTYPE_H + +#include + +#include "ccl.h" +#include "jrx-intern.h" + +extern jrx_ccl* local_ccl_lower(jrx_ccl_group* group); +extern jrx_ccl* local_ccl_upper(jrx_ccl_group* group); +extern jrx_ccl* local_ccl_word(jrx_ccl_group* group); +extern jrx_ccl* local_ccl_digit(jrx_ccl_group* group); +extern jrx_ccl* local_ccl_blank(jrx_ccl_group* group); + +static inline int _isword(jrx_char cp) +{ + return isalnum(cp) || cp == '_'; +} + +static inline int local_word_boundary(jrx_char* prev, jrx_char current) +{ + return _isword(current) ? (prev ? ! _isword(*prev) : 1) : 0; +} + +#endif diff --git a/hilti/src/3rdparty/justrx/src/jrx-intern.h b/hilti/src/3rdparty/justrx/src/jrx-intern.h new file mode 100644 index 000000000..63013d44c --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/jrx-intern.h @@ -0,0 +1,44 @@ +// $Id$ +// +// Every file part the library should include this header first so that GC +// can be set up correctly, if necessary. + +#ifndef JRX_INTERN_H +#define JRX_INTERN_H + +#include "jrx.h" +#include "mem-mgt.h" + +#include + +// Predefined constants. +static const jrx_char JRX_CHAR_MAX = UINT32_MAX; // Max. codepoint. +static const jrx_offset JRX_OFFSET_MAX = INT32_MAX; // Max. offset value. + +// Matching options. +typedef uint8_t jrx_option; +static const jrx_option JRX_OPTION_NONE = 0; +static const jrx_option JRX_OPTION_CASE_INSENSITIVE = 1 << 0; // Match case-insentive. +static const jrx_option JRX_OPTION_LAZY = 1 << 1; // Compute DFA lazily. +static const jrx_option JRX_OPTION_DEBUG = 1 << 2; // Print debug information. +static const jrx_option JRX_OPTION_NO_CAPTURE = 1 << 3; // Do not capture subgroups. +static const jrx_option JRX_OPTION_STD_MATCHER = 1 << 4; // Use the standard matcher. +static const jrx_option JRX_OPTION_DONT_ANCHOR = 1 << 5; // Don't anchor RE at the beginning. +static const jrx_option JRX_OPTION_FIRST_MATCH = 1 << 6; // Take first match, rather than longest. +// static const jrx_option OPTIONS_INCREMENTAL_DFA = 1 << 4; // Build DFA incrementally. + +// Predefined standard character classes. +typedef enum { + JRX_STD_CCL_NONE, + JRX_STD_CCL_EPSILON, + JRX_STD_CCL_ANY, + JRX_STD_CCL_LOWER, + JRX_STD_CCL_UPPER, + JRX_STD_CCL_WORD, + JRX_STD_CCL_DIGIT, + JRX_STD_CCL_BLANK, + + JRX_STD_CCL_NUM, // Count number of std CCLs. +} jrx_std_ccl; + +#endif diff --git a/hilti/src/3rdparty/justrx/src/jrx.c b/hilti/src/3rdparty/justrx/src/jrx.c new file mode 100644 index 000000000..e7fe81cb7 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/jrx.c @@ -0,0 +1,394 @@ + +#include "dfa-interpreter-min.h" +#include "dfa-interpreter-std.h" +#include "jrx-intern.h" + +// Collects the right options based +static jrx_option _options(jrx_regex_t* preg) +{ + int cflags = preg->cflags; + + if ( ! (cflags & REG_EXTENDED) ) + preg->errmsg = "REG_BASIC syntax is not supported"; + + if ( cflags & REG_ICASE ) + preg->errmsg = "REG_ICASE not supported at this time"; + + if ( cflags & REG_NEWLINE ) + preg->errmsg = "REG_NEWLINE not supported at this time"; + + if ( preg->errmsg ) + return REG_NOTSUPPORTED; + + jrx_option options = 0; + + if ( cflags & REG_DEBUG ) + options |= JRX_OPTION_DEBUG; + + if ( ! (cflags & REG_ANCHOR) ) + options |= JRX_OPTION_DONT_ANCHOR; + + if ( cflags & REG_NOSUB ) + options |= JRX_OPTION_NO_CAPTURE; + else + options |= JRX_OPTION_STD_MATCHER; + + if ( cflags & REG_STD_MATCHER ) + options |= JRX_OPTION_STD_MATCHER; + + if ( cflags & REG_LAZY ) + options |= JRX_OPTION_LAZY; + + if ( cflags & REG_FIRST_MATCH ) + options |= JRX_OPTION_FIRST_MATCH; + + return options; +} + +static inline void _clear_pmatch(size_t nmatch, jrx_regmatch_t pmatch[], int first_zero) +{ + int i; + for ( i = 0; i < nmatch; i++ ) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + + if ( first_zero && nmatch > 0 ) + pmatch[0].rm_so = pmatch[0].rm_eo = 0; +} + +static inline jrx_match_accept _pick_accept(set_match_accept* accepts) +{ + jrx_match_accept result = {0, 0}; + jrx_offset min = JRX_OFFSET_MAX; + jrx_offset min_len = 0; + // We take the left-most match. + set_for_each(match_accept, accepts, acc) + { + if ( ! acc.tags ) { + if ( ! result.aid ) + result = acc; + continue; + } + + int len = acc.tags[1] - acc.tags[0]; + + if ( acc.tags[0] < min || (acc.tags[0] == min && len > min_len) ) { + result = acc; + min = acc.tags[0]; + min_len = len; + } + } + + return result; +} + +// Returns: +// +// 0: matching failed and can't be resumed. +// >0: accept with this ID (if multiple, it's undefined which). +// -1: partial but not full match yet. +static int _regexec_partial_std(const jrx_regex_t* preg, const char* buffer, unsigned int len, + jrx_assertion first, jrx_assertion last, jrx_match_state* ms, + int find_partial_matches) +{ + const char* p; + for ( p = buffer; len; len-- ) { + jrx_assertion assertions = JRX_ASSERTION_NONE; + + if ( p == buffer ) + assertions |= first; + + if ( len == 1 ) + assertions |= last; + + if ( jrx_match_state_advance(ms, *p++, assertions) == 0 ) { + jrx_match_accept acc = _pick_accept(ms->accepts); + return acc.aid ? acc.aid : 0; + } + } + + if ( ! find_partial_matches && jrx_can_transition(ms) && ! (preg->cflags & REG_FIRST_MATCH) ) + return -1; + + jrx_match_accept acc = _pick_accept(ms->accepts); + return acc.aid ? acc.aid : -1; +} + +// Returns: +// +// 0: matching failed and can't be resumed. +// >0: accept with this ID (if multiple, it's undefined which). +// -1: partial but not full match yet. +static int _regexec_partial_min(const jrx_regex_t* preg, const char* buffer, unsigned int len, + jrx_assertion first, jrx_assertion last, jrx_match_state* ms, + int find_partial_matches) +{ + jrx_offset eo = ms->offset; + + const char* p; + for ( p = buffer; len; --len ) { + jrx_assertion assertions = JRX_ASSERTION_NONE; + + if ( p == buffer ) + assertions |= first; + + if ( len == 1 ) + assertions |= last; + + jrx_accept_id rc = jrx_match_state_advance_min(ms, *p++, assertions); + + if ( ! rc ) { + ms->offset = eo; + return ms->acc > 0 ? ms->acc : 0; + } + + if ( rc > 0 ) { + eo = ms->offset; + ms->acc = rc; + + if ( preg->cflags & REG_FIRST_MATCH || ! jrx_can_transition(ms) ) + return ms->acc; + } + } + + ms->offset = eo; + + if ( ! find_partial_matches && jrx_can_transition(ms) ) + return -1; + + return ms->acc; +} + +void jrx_regset_init(jrx_regex_t* preg, int nmatch, int cflags) +{ + // Determine whether we will use the standard or the minimal matcher, and + // if the former enforce the corresponding flag to be set. + if ( nmatch != 0 && ! (cflags & REG_NOSUB) ) + cflags |= REG_STD_MATCHER; + + preg->re_nsub = 0; + preg->nmatch = nmatch; + preg->cflags = cflags; + preg->nfa = 0; + preg->dfa = 0; + preg->errmsg = 0; +} + +int jrx_regset_add(jrx_regex_t* preg, const char* pattern, unsigned int len) +{ + jrx_option options = _options(preg); + + if ( options == REG_NOTSUPPORTED ) + return REG_BADPAT; + + if ( ! preg->nfa ) + preg->nfa = nfa_compile(pattern, len, options, preg->nmatch, &preg->errmsg); + + else { + preg->nfa = nfa_compile_add(preg->nfa, pattern, len, &preg->errmsg); + nfa_remove_epsilons(preg->nfa); + } + + return preg->errmsg ? REG_BADPAT : REG_OK; +} + +int jrx_regset_finalize(jrx_regex_t* preg) +{ + jrx_dfa* dfa = dfa_from_nfa(preg->nfa); + if ( ! dfa ) + return REG_EMEM; + + preg->dfa = dfa; + preg->re_nsub = dfa->max_capture; + + return REG_OK; +} + +int jrx_regcomp(jrx_regex_t* preg, const char* pattern, int cflags) +{ + jrx_regset_init(preg, -1, cflags); + + int rc = jrx_regset_add(preg, pattern, strlen(pattern)); + if ( rc != REG_OK ) + return rc; + + return jrx_regset_finalize(preg); +} + +// Returns: +// +// 0: matching failed and can't be resumed. +// >0: accept with this ID (if multiple, it's undefined which). +// -1: partial but not full match yet. +int jrx_regexec_partial(const jrx_regex_t* preg, const char* buffer, unsigned int len, + jrx_assertion first, jrx_assertion last, jrx_match_state* ms, + int find_partial_matches) +{ + int rc = 0; + + if ( preg->cflags & REG_STD_MATCHER ) + rc = _regexec_partial_std(preg, buffer, len, first, last, ms, find_partial_matches); + else + rc = _regexec_partial_min(preg, buffer, len, first, last, ms, find_partial_matches); + + return rc; +} + +int jrx_reggroups(const jrx_regex_t* preg, jrx_match_state* ms, size_t nmatch, + jrx_regmatch_t pmatch[]) +{ + if ( ! (preg->cflags & REG_STD_MATCHER) || (preg->dfa->options & JRX_OPTION_NO_CAPTURE) ) { + _clear_pmatch(nmatch, pmatch, 1); + return REG_OK; // Fail silently. + } + + if ( ! set_match_accept_size(ms->accepts) ) { + _clear_pmatch(nmatch, pmatch, 0); + return REG_NOMATCH; + } + + jrx_match_accept acc = _pick_accept(ms->accepts); + jrx_offset* tags = acc.tags; + assert(tags); + + int i; + + for ( i = 0; i < nmatch; i++ ) { + if ( i <= preg->dfa->max_capture && i * 2 + 1 <= preg->dfa->max_tag && tags[i * 2] > 0 && + tags[i * 2 + 1] > 0 ) { + pmatch[i].rm_so = ms->begin + tags[i * 2] - 1; + pmatch[i].rm_eo = ms->begin + tags[i * 2 + 1] - 1; + } + else + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + } + + return REG_OK; +} + +int jrx_regexec(const jrx_regex_t* preg, const char* string, size_t nmatch, jrx_regmatch_t pmatch[], + int eflags) +{ + if ( eflags & (REG_NOTEOL | REG_NOTBOL) ) + return REG_NOTSUPPORTED; + + if ( ! (string && *string) ) { + _clear_pmatch(nmatch, pmatch, 1); + return 0; + } + + jrx_match_state ms; + jrx_match_state_init(preg, 0, &ms); + + jrx_assertion first = JRX_ASSERTION_BOL | JRX_ASSERTION_BOD; + jrx_assertion last = JRX_ASSERTION_EOL | JRX_ASSERTION_EOD; + + int rc = jrx_regexec_partial(preg, string, strlen(string), first, last, &ms, 1); + + if ( rc <= 0 ) { + jrx_match_state_done(&ms); + return REG_NOMATCH; + } + + rc = jrx_reggroups(preg, &ms, nmatch, pmatch); + jrx_match_state_done(&ms); + return rc; +} + +void jrx_regfree(jrx_regex_t* preg) +{ + if ( preg->nfa ) + nfa_delete(preg->nfa); + + if ( preg->dfa ) + dfa_delete(preg->dfa); +} + +size_t jrx_regerror(int errcode, const jrx_regex_t* preg, char* errbuf, size_t errbuf_size) +{ + char buffer[127]; + + const char* msg = 0; + switch ( errcode ) { + case REG_NOTSUPPORTED: + msg = "feature not supported"; + break; + + case REG_BADPAT: + msg = "bad pattern"; + break; + + case REG_NOMATCH: + msg = "no match"; + break; + + default: + msg = "unknown error code for regerror()"; + } + + if ( preg->errmsg ) { + snprintf(buffer, sizeof(buffer), "%s: %s", msg, preg->errmsg); + msg = buffer; + } + + if ( errbuf && errbuf_size ) { + strncpy(errbuf, msg, errbuf_size); + errbuf[errbuf_size - 1] = '\0'; + } + + return strlen(msg); +} + +int jrx_num_groups(jrx_regex_t* preg) +{ + return preg->dfa->max_capture + 1; +} + +int jrx_is_anchored(jrx_regex_t* preg) +{ + jrx_nfa_state* initial = preg->nfa->initial; + + if ( ! initial ) + return 0; + + vec_for_each(nfa_transition, initial->trans, trans) + { + jrx_ccl* ccl = vec_ccl_get(preg->nfa->ctx->ccls->ccls, trans.ccl); + if ( ! (ccl->assertions & JRX_ASSERTION_BOL) ) + return 0; + } + + return 1; +} + +int jrx_can_transition(jrx_match_state* ms) +{ + jrx_dfa_state* state = vec_dfa_state_get(ms->dfa->states, ms->state); + + if ( ! state ) { + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, "> can_transition: 0\n"); + + return 0; + } + + int can = vec_dfa_transition_size(state->trans); + if ( ms->dfa->options & JRX_OPTION_DEBUG ) + fprintf(stderr, "> can_transition: %d (%d)\n", (can != 0), can); + + return can; +} + +int jrx_current_accept(jrx_match_state* ms) +{ + if ( (ms->dfa->options & JRX_OPTION_STD_MATCHER) ) { + if ( ! ms->accepts ) + return 0; + + jrx_match_accept acc = _pick_accept(ms->accepts); + return acc.aid ? acc.aid : 0; + } + + + jrx_dfa_state* state = dfa_get_state(ms->dfa, ms->state); + return state->accepts ? vec_dfa_accept_get(state->accepts, 0).aid : 0; +} diff --git a/hilti/src/3rdparty/justrx/src/jrx.h b/hilti/src/3rdparty/justrx/src/jrx.h new file mode 100644 index 000000000..c7654c2e6 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/jrx.h @@ -0,0 +1,157 @@ +// $Id$ +// +/// High-level regexp interface, partially matching POSIX functions. + +#ifndef JRX_H +#define JRX_H + +#include +#include + +// Predefined types. +typedef uint32_t jrx_char; ///< A single codepoint. +typedef int32_t jrx_offset; ///< Offset in input stream. +typedef int16_t jrx_accept_id; ///< ID for an accepting state. +typedef uint32_t jrx_nfa_state_id; // ID for an NFA state. +typedef uint32_t jrx_dfa_state_id; // ID for a DFA state. +typedef uint16_t jrx_ccl_id; // ID for a CCL. + +typedef uint16_t jrx_assertion; ///< Type for zero-width assertions. +static const jrx_assertion JRX_ASSERTION_NONE = 0; +static const jrx_assertion JRX_ASSERTION_BOL = 1 << 1; ///< Beginning of line. +static const jrx_assertion JRX_ASSERTION_EOL = 1 << 2; ///< End of line. +static const jrx_assertion JRX_ASSERTION_BOD = 1 << 3; ///< Beginning of data. +static const jrx_assertion JRX_ASSERTION_EOD = 1 << 4; ///< End of data. +static const jrx_assertion JRX_ASSERTION_WORD_BOUNDARY = 1 << 5; ///< A word boundary. +static const jrx_assertion JRX_ASSERTION_NOT_WORD_BOUNDARY = 1 << 6; ///< Not a word boundary. +static const jrx_assertion JRX_ASSERTION_CUSTOM1 = 1 << 12; ///< Assertion for custom usage. +static const jrx_assertion JRX_ASSERTION_CUSTOM2 = 1 << 13; ///< Assertion for custom usage. +static const jrx_assertion JRX_ASSERTION_CUSTOM3 = 1 << 14; ///< Assertion for custom usage. +static const jrx_assertion JRX_ASSERTION_CUSTOM4 = 1 << 15; ///< Assertion for custom usage. + +struct jrx_nfa; +struct jrx_dfa; +struct set_match_accept; +struct jrx_match_state; +typedef struct jrx_match_state jrx_match_state; +typedef struct jrx_regex_t jrx_regex_t; + +struct jrx_match_state { + jrx_offset offset; ///< Offset of next input byte. + jrx_offset begin; // Offset of first cp; will be added to pmatch. + struct jrx_dfa* dfa; // The DFA we're matching with. + jrx_dfa_state_id state; // Current state. + jrx_char previous; // Previous code point seen (valid iff offset > 0) + int cflags; // RE_* flags that were used for compilation. + + // The following are only used with the full matcher. + struct set_match_accept* accepts; // Accepts we have encountered so far. + int current_tags; // Current set of position of tags (0 or 1). + jrx_offset* tags1; // 1st & 2nd set of position of tags; (we use + jrx_offset* tags2; // a double-buffering scheme here). + int tags1_size; // Current sizes of 1st and 2nd tags sets. + int tags2_size; + + // The following are only used with the minimal matcher. + jrx_accept_id acc; +}; + +struct jrx_regex_t { + size_t re_nsub; ///< Number of capture expressions in regular expression (POSIX). + int cflags; // RE_* flags for compilation. + int nmatch; // Max. number of subexpression caller is interested in; -1 for all. + struct jrx_nfa* nfa; // Compiled NFA, or NULL. + struct jrx_dfa* dfa; // Compiled DFA, or NULL. + const char* errmsg; // Most recent error message, or NULL if none. +}; + +typedef jrx_offset regoff_t; + +typedef struct jrx_regmatch_t { + regoff_t rm_so; //< Zero-based start offset of match (POSIX). + regoff_t + rm_eo; //< End offset of match (POSIX). It locates the first byte after the match. (POSIX). +} jrx_regmatch_t; + +// POSIX options. We use macros here for compatibility with code using +// ifdef's on them and/or expecting integers. +#define REG_BASIC 0 // sic! (but not supported anyway) +#define REG_EXTENDED (1 << 0) ///< "Extended" regular expression syntax (we only one we support). +#define REG_NOSUB (1 << 1) + +// FIXME: The following are not implemented currently. +#define REG_ICASE (1 << 2) +#define REG_NEWLINE (1 << 3) +#define REG_NOTBOL (1 << 4) +#define REG_NOTEOL (1 << 5) + +// Non-standard options. +#define REG_DEBUG (1 << 6) //< Enable debugging output to stderr. +#define REG_STD_MATCHER \ + (1 << 7) //< Force usage of the (slower) standard matcher even with REG_NOSUB. +#define REG_ANCHOR \ + (1 << 8) //< Anchor matching at beginning. The effect is that of an implicit '^' at the + // beginning. +#define REG_LAZY (1 << 9) //< Build DFA incrementally. +#define REG_FIRST_MATCH (1 << 10) //< Take first match, rather than longest. + +// Non-standard error codes.. +#define REG_OK 0 //< Everything is fine. +#define REG_NOTSUPPORTED 1 //< A non-supported feature has been used. + +// POSIX error codes. +#define REG_BADPAT 3 //< A bad pattern was giving for compilation. +#define REG_NOMATCH 4 //< No match has been found. +#define REG_EMEM 5 //< Running out of memory. + +// We actually do not raise these POSIX errors but define them for +// completeness. +#define REG_ECOLLATE 10 +#define REG_ECTYPE 11 +#define REG_EESCAPE 12 +#define REG_ESUBREG 13 +#define REG_EBRACK 14 +#define REG_EPAREN 15 +#define REG_EBRACE 16 +#define REG_BADBR 17 +#define REG_ERANGE 18 +#define REG_ESPACE 19 +#define REG_BADRPT 20 +#define REG_ENEWLINE 21 +#define REG_ENULL 22 +#define REG_ECOUNT 23 +#define REG_BADESC 24 +#define REG_EHUNG 25 +#define REG_EBUS 26 +#define REG_EFAULT 27 +#define REG_EFLAGS 28 +#define REG_EDELIM 29 + +// These are POSIX compatible. +extern int jrx_regcomp(jrx_regex_t* preg, const char* pattern, int cflags); +extern size_t jrx_regerror(int errcode, const jrx_regex_t* preg, char* errbuf, size_t errbuf_size); +extern int jrx_regexec(const jrx_regex_t* preg, const char* string, size_t nmatch, + jrx_regmatch_t pmatch[], int eflags); +extern void jrx_regfree(jrx_regex_t* preg); + +// These are non-POSIX extensions. +extern void jrx_regset_init(jrx_regex_t* preg, int nmatch, int cflags); +extern void jrx_regset_done(jrx_regex_t* preg, int cflags); +extern int jrx_regset_add(jrx_regex_t* preg, const char* pattern, unsigned int len); +extern int jrx_regset_finalize(jrx_regex_t* preg); +extern int jrx_regexec_partial(const jrx_regex_t* preg, const char* buffer, unsigned int len, + jrx_assertion first, jrx_assertion last, jrx_match_state* ms, + int find_partial_matches); +extern int jrx_reggroups(const jrx_regex_t* preg, jrx_match_state* ms, size_t nmatch, + jrx_regmatch_t pmatch[]); +extern int jrx_num_groups(jrx_regex_t* preg); +extern int jrx_is_anchored(jrx_regex_t* preg); +extern int jrx_can_transition(jrx_match_state* ms); +extern int jrx_current_accept(jrx_match_state* ms); + +extern jrx_match_state* jrx_match_state_init(const jrx_regex_t* preg, jrx_offset begin, + jrx_match_state* ms); +extern void jrx_match_state_copy(jrx_match_state* from, jrx_match_state* to); // supports only min-matcher state +extern void jrx_match_state_done(jrx_match_state* ms); + +#endif diff --git a/hilti/src/3rdparty/justrx/src/khash.h b/hilti/src/3rdparty/justrx/src/khash.h new file mode 100644 index 000000000..24467556b --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/khash.h @@ -0,0 +1,316 @@ +/* The MIT License + + Copyright (c) 2008, by Attractive Chaos + + 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. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + if (!ret) kh_del(32, h, k); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +#define AC_VERSION_KHASH_H "0.2.2" + +#include +#include +#include + +typedef uint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_HASH_PRIME_SIZE 32 +static const uint32_t __ac_prime_list[__ac_HASH_PRIME_SIZE] = +{ + 0ul, 3ul, 11ul, 23ul, 53ul, + 97ul, 193ul, 389ul, 769ul, 1543ul, + 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, + 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, + 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, + 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, + 3221225473ul, 4294967291ul +}; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +static const double __ac_HASH_UPPER = 0.77; + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + typedef struct { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + uint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; \ + static inline kh_##name##_t *kh_init_##name() { \ + return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \ + } \ + static inline void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + free(h->keys); free(h->flags); \ + free(h->vals); \ + free(h); \ + } \ + } \ + static inline void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(uint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + static inline khint_t kh_get_##name(kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t inc, k, i, last; \ + k = __hash_func(key); i = k % h->n_buckets; \ + inc = 1 + k % (h->n_buckets - 1); last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ + else i += inc; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + static inline void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { \ + uint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + khint_t t = __ac_HASH_PRIME_SIZE - 1; \ + while (__ac_prime_list[t] > new_n_buckets) --t; \ + new_n_buckets = __ac_prime_list[t+1]; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ + else { \ + new_flags = (uint32_t*)malloc(((new_n_buckets>>4) + 1) * sizeof(uint32_t)); \ + memset(new_flags, 0xaa, ((new_n_buckets>>4) + 1) * sizeof(uint32_t)); \ + if (h->n_buckets < new_n_buckets) { \ + h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) \ + h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + } \ + } \ + if (j) { \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { \ + khint_t inc, k, i; \ + k = __hash_func(key); \ + i = k % new_n_buckets; \ + inc = 1 + k % (new_n_buckets - 1); \ + while (!__ac_isempty(new_flags, i)) { \ + if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ + else i += inc; \ + } \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); \ + } else { \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { \ + h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) \ + h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + free(h->flags); \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + } \ + static inline khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { \ + if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \ + else kh_resize_##name(h, h->n_buckets + 1); \ + } \ + { \ + khint_t inc, k, i, site, last; \ + x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \ + if (__ac_isempty(h->flags, i)) x = i; \ + else { \ + inc = 1 + k % (h->n_buckets - 1); last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ + else i += inc; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; \ + return x; \ + } \ + static inline void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +#define kh_int_hash_func(key) (uint32_t)(key) +#define kh_int_hash_equal(a, b) (a == b) +#define kh_int64_hash_func(key) (uint32_t)((key)>>33^(key)^(key)<<11) +#define kh_int64_hash_equal(a, b) (a == b) +static inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = *s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; + return h; +} +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other necessary macros... */ + +#define khash_t(name) kh_##name##_t + +#define kh_init(name) kh_init_##name() +#define kh_destroy(name, h) kh_destroy_##name(h) +#define kh_clear(name, h) kh_clear_##name(h) +#define kh_resize(name, h, s) kh_resize_##name(h, s) +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) +#define kh_get(name, h, k) kh_get_##name(h, k) +#define kh_del(name, h, k) kh_del_##name(h, k) + +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) +#define kh_key(h, x) ((h)->keys[x]) +#define kh_val(h, x) ((h)->vals[x]) +#define kh_value(h, x) ((h)->vals[x]) +#define kh_begin(h) (khint_t)(0) +#define kh_end(h) ((h)->n_buckets) +#define kh_size(h) ((h)->size) +#define kh_n_buckets(h) ((h)->n_buckets) + +/* More conenient interfaces */ + +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, uint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, uint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, uint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, uint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/hilti/src/3rdparty/justrx/src/mem-mgt.h b/hilti/src/3rdparty/justrx/src/mem-mgt.h new file mode 100644 index 000000000..112548006 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/mem-mgt.h @@ -0,0 +1,18 @@ + +// If we are compiled as part of HILTI, use it's memory management functions. + +#ifndef JRX_MEMMGT_H +#define JRX_MEMMGT_H + +#ifdef JRX_USE_HILTI + +#include "memory_.h" + +#define malloc hlt_malloc +#define calloc hlt_calloc +#define realloc hlt_realloc_no_init +#define free hlt_free + +#endif + +#endif diff --git a/hilti/src/3rdparty/justrx/src/nfa.c b/hilti/src/3rdparty/justrx/src/nfa.c new file mode 100644 index 000000000..ba2bd84d8 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/nfa.c @@ -0,0 +1,621 @@ +// $Id$ + +#include "nfa.h" +#include "jrx-intern.h" + +#include +#include + +#include +#include +#include +#include + +/** \addtogroup NFA */ +//@{ + + + +static jrx_nfa_state* _nfa_state_create(jrx_nfa_context* ctx) +{ + jrx_nfa_state* state = (jrx_nfa_state*)malloc(sizeof(jrx_nfa_state)); + if ( ! state ) + return 0; + + state->id = vec_nfa_state_append(ctx->states, state); + state->accepts = 0; + state->trans = vec_nfa_transition_create(0); + return state; +} + +// Not exposed. Delete the context instead. +static void _nfa_state_delete(jrx_nfa_state* state) +{ + vec_for_each(nfa_transition, state->trans, trans) + { + if ( trans.tags ) + set_tag_delete(trans.tags); + } + + vec_nfa_transition_delete(state->trans); + + if ( state->accepts ) { + vec_for_each(nfa_accept, state->accepts, acc) + { + if ( acc.tags ) + set_tag_delete(acc.tags); + } + + vec_nfa_accept_delete(state->accepts); + } + + free(state); +} + +static void _nfa_state_closure(jrx_nfa_context* ctx, jrx_nfa_state* state, + set_nfa_state_id* closure) +{ + if ( set_nfa_state_id_contains(closure, state->id) ) + return; + + set_nfa_state_id_insert(closure, state->id); + + vec_for_each(nfa_transition, state->trans, trans) + _nfa_state_closure(ctx, vec_nfa_state_get(ctx->states, trans.succ), closure); +} + +static jrx_nfa_state* _nfa_state_deep_copy(jrx_nfa_context* ctx, jrx_nfa_state* state, + vec_nfa_state* copies) +{ + jrx_nfa_state* copy = vec_nfa_state_get(copies, state->id); + + if ( copy ) + return copy; + + copy = _nfa_state_create(ctx); + + if ( ! copy ) + return 0; + + vec_nfa_state_set(copies, state->id, copy); + + copy->accepts = state->accepts ? vec_nfa_accept_copy(state->accepts) : 0; + + vec_for_each(nfa_transition, state->trans, trans) + { + jrx_nfa_state* succ = vec_nfa_state_get(ctx->states, trans.succ); + jrx_nfa_state* nsucc = _nfa_state_deep_copy(ctx, succ, copies); + + assert(nsucc); + jrx_nfa_transition ntrans = {trans.ccl, nsucc->id, + trans.tags ? set_tag_copy(trans.tags) : 0}; + vec_nfa_transition_append(copy->trans, ntrans); + } + + return copy; +} + +static void _nfa_state_add_trans(jrx_nfa_state* state, jrx_nfa_state* succ, set_tag* tags, + jrx_ccl* ccl) +{ + assert(succ && state && state->trans); + jrx_nfa_transition ntrans = {ccl->id, succ->id, tags ? set_tag_copy(tags) : 0}; + vec_nfa_transition_append(state->trans, ntrans); +} + +static jrx_nfa* _nfa_deep_copy(jrx_nfa* nfa) +{ + vec_nfa_state* copies = vec_nfa_state_create(0); + + jrx_nfa_state* ninitial = _nfa_state_deep_copy(nfa->ctx, nfa->initial, copies); + jrx_nfa_state* nfinal = _nfa_state_deep_copy(nfa->ctx, nfa->final, copies); + + jrx_nfa* copy = nfa_create(nfa->ctx, ninitial, nfinal); + copy->initial_tags = nfa->initial_tags ? set_tag_copy(nfa->initial_tags) : 0; + + vec_nfa_state_delete(copies); + + return copy; +} + +jrx_nfa_context* nfa_context_create(jrx_option options, int8_t nmatch) +{ + jrx_nfa_context* ctx = (jrx_nfa_context*)malloc(sizeof(jrx_nfa_context)); + ctx->refcnt = 0; + ctx->options = options; + ctx->nmatch = nmatch >= 0 ? nmatch : INT8_MAX; + ctx->max_tag = -1; + ctx->max_capture = 0; // We always have one implicitly. This is adjusted only by re_parse.y + ctx->max_accept = 0; // 0 is "no accept". + ctx->ccls = ccl_group_create(); + ctx->states = vec_nfa_state_create(0); + return ctx; +} + +void nfa_context_delete(jrx_nfa_context* ctx) +{ + if ( ! ctx ) + return; + + ccl_group_delete(ctx->ccls); + + vec_for_each(nfa_state, ctx->states, state) _nfa_state_delete(state); + + vec_nfa_state_delete(ctx->states); + free(ctx); +} + +jrx_nfa* nfa_create(jrx_nfa_context* ctx, jrx_nfa_state* initial, jrx_nfa_state* final) +{ + jrx_nfa* nfa = (jrx_nfa*)malloc(sizeof(jrx_nfa)); + nfa->ctx = ctx; + nfa->initial_tags = 0; + nfa->initial = initial; + nfa->final = final; + ++ctx->refcnt; + return nfa; +} + +void nfa_delete(jrx_nfa* nfa) +{ + if ( ! nfa ) + return; + + if ( --nfa->ctx->refcnt == 0 ) + nfa_context_delete(nfa->ctx); + + if ( nfa->initial_tags ) + set_tag_delete(nfa->initial_tags); + + free(nfa); +} + +jrx_nfa* nfa_set_accept(jrx_nfa* nfa, jrx_accept_id accept) +{ + assert(nfa->initial && nfa->final); + + jrx_nfa_accept acc = {0, accept, 0}; + + if ( ! nfa->final->accepts ) + nfa->final->accepts = vec_nfa_accept_create(0); + + vec_nfa_accept_append(nfa->final->accepts, acc); + + if ( accept > nfa->ctx->max_accept ) + nfa->ctx->max_accept = accept; + + return nfa; +} + +jrx_nfa* nfa_set_capture(jrx_nfa* nfa, uint8_t group) +{ + assert(nfa->initial && nfa->final); + jrx_nfa_context* ctx = nfa->ctx; + + if ( group >= ctx->nmatch ) + // Uninteresting group. + return nfa; + + if ( group * 2 + 1 > ctx->max_tag ) + ctx->max_tag = group * 2 + 1; + + if ( ! nfa->initial_tags ) + nfa->initial_tags = set_tag_create(0); + + jrx_nfa* eps = nfa_empty(ctx); + eps->initial_tags = set_tag_create(0); + + jrx_tag t1 = {group * 2, -5}; + jrx_tag t2 = {group * 2 + 1, 5}; + + set_tag_insert(nfa->initial_tags, t1); + set_tag_insert(eps->initial_tags, t2); + + jrx_nfa* nnfa = nfa_concat(nfa, eps, 0); + + return nnfa; +} + +jrx_nfa* nfa_empty(jrx_nfa_context* ctx) +{ + jrx_nfa_state* s = _nfa_state_create(ctx); + jrx_nfa* nfa = nfa_create(ctx, s, s); + return nfa; +} + +jrx_nfa* nfa_from_ccl(jrx_nfa_context* ctx, jrx_ccl* ccl) +{ + jrx_nfa* nfa1 = nfa_empty(ctx); + jrx_nfa* nfa2 = nfa_empty(ctx); + return nfa_concat(nfa1, nfa2, ccl); +} + +jrx_nfa* nfa_concat(jrx_nfa* nfa1, jrx_nfa* nfa2, jrx_ccl* ccl) +{ + assert(nfa1->ctx == nfa2->ctx); + jrx_nfa_context* ctx = nfa1->ctx; + + if ( ! ccl ) + ccl = ccl_epsilon(ctx->ccls); + + _nfa_state_add_trans(nfa1->final, nfa2->initial, nfa2->initial_tags, ccl); + nfa1->final = nfa2->final; + + nfa2->initial = 0; + nfa2->final = 0; + nfa_delete(nfa2); + + return nfa1; +} + +jrx_nfa* nfa_alternative(jrx_nfa* nfa1, jrx_nfa* nfa2) +{ + assert(nfa1->ctx == nfa2->ctx); + jrx_nfa_context* ctx = nfa1->ctx; + + jrx_nfa_state* eps1 = _nfa_state_create(ctx); + _nfa_state_add_trans(eps1, nfa1->initial, nfa1->initial_tags, ccl_epsilon(ctx->ccls)); + _nfa_state_add_trans(eps1, nfa2->initial, nfa2->initial_tags, ccl_epsilon(ctx->ccls)); + + jrx_nfa_state* eps2 = _nfa_state_create(ctx); + _nfa_state_add_trans(nfa1->final, eps2, 0, ccl_epsilon(ctx->ccls)); + _nfa_state_add_trans(nfa2->final, eps2, 0, ccl_epsilon(ctx->ccls)); + + jrx_nfa* nfa = nfa_create(ctx, eps1, eps2); + + nfa_delete(nfa1); + + if ( nfa1 != nfa2 ) + nfa_delete(nfa2); + + return nfa; +} + +jrx_nfa* nfa_iterate(jrx_nfa* nfa, int min, int max) +{ + assert(max >= min || max == -1); + + jrx_nfa_context* ctx = nfa->ctx; + jrx_nfa* templ = _nfa_deep_copy(nfa); + + if ( min < 0 ) + min = 0; + + if ( min == 0 && max == 0 ) { + nfa_delete(nfa); + return nfa_empty(ctx); + } + + jrx_nfa* all = 0; + + if ( min > 1 ) { + all = nfa; + int i; + for ( i = 0; i < min - 1; i++ ) + all = nfa_concat(all, _nfa_deep_copy(templ), 0); + } + else + nfa_delete(nfa); + + if ( max >= 0 ) { + jrx_nfa* optional = nfa_alternative(_nfa_deep_copy(templ), nfa_empty(ctx)); + + int i; + for ( i = max - min; i > 0; i-- ) + all = all ? nfa_concat(all, _nfa_deep_copy(optional), 0) : optional; + } + + else { + jrx_nfa* closure = _nfa_deep_copy(templ); + _nfa_state_add_trans(closure->final, closure->initial, /* closure->initial_tags */ 0, + ccl_epsilon(ctx->ccls)); + all = all ? nfa_concat(all, closure, 0) : closure; + } + + if ( min == 0 ) { + jrx_nfa* optional = nfa_alternative(all, nfa_empty(ctx)); + all = optional; + } + + nfa_delete(templ); + return all; +} + +void _nfa_state_follow_epsilons(jrx_nfa_context* ctx, jrx_nfa_state* state, + set_nfa_state_id* closure, vec_nfa_transition* ntrans, + set_tag** tags, vec_nfa_accept** accepts, jrx_assertion assertions) +{ + if ( ! state ) + return; + + if ( set_nfa_state_id_contains(closure, state->id) ) + return; + + set_nfa_state_id_insert(closure, state->id); + + if ( state->accepts && state->accepts != *accepts ) { + vec_for_each(nfa_accept, state->accepts, acc) + { + set_tag* ntags = 0; + + if ( acc.tags || *tags ) { + ntags = set_tag_create(0); + + if ( acc.tags ) + set_tag_join(ntags, acc.tags); + + if ( *tags ) + set_tag_join(ntags, *tags); + } + + if ( ! *accepts ) + *accepts = vec_nfa_accept_create(0); + + jrx_nfa_accept nacc = {acc.assertions | assertions, acc.aid, ntags}; + vec_nfa_accept_append(*accepts, nacc); + } + } + + vec_for_each(nfa_transition, state->trans, trans) + { + jrx_ccl* ccl = vec_ccl_get(ctx->ccls->ccls, trans.ccl); + + if ( ! ccl_is_epsilon(ccl) ) { + // Add a new transition to this state. + ccl = ccl_add_assertions(ccl, assertions); + + if ( trans.tags ) { + if ( ! *tags ) + *tags = set_tag_create(0); + + set_tag_join(*tags, trans.tags); + } + + jrx_nfa_transition t = {ccl->id, trans.succ, *tags ? set_tag_copy(*tags) : 0}; + vec_nfa_transition_append(ntrans, t); + } + + else { + // Another epsilon transition, recurse. + set_tag* ntags = *tags ? set_tag_copy(*tags) : 0; + + if ( trans.tags ) { + if ( ! ntags ) + ntags = set_tag_copy(trans.tags); + else + set_tag_join(ntags, trans.tags); + } + + jrx_nfa_state* succ = vec_nfa_state_get(ctx->states, trans.succ); + + _nfa_state_follow_epsilons(ctx, succ, closure, ntrans, &ntags, accepts, + assertions | ccl->assertions); + + if ( ntags ) + set_tag_delete(ntags); + } + } +} + +void nfa_remove_epsilons(jrx_nfa* nfa) +{ + jrx_nfa_context* ctx = nfa->ctx; + + vec_for_each(nfa_state, ctx->states, state) + { + vec_nfa_transition* ntrans = vec_nfa_transition_create(0); + + vec_for_each(nfa_transition, state->trans, trans) + { + jrx_ccl* ccl = vec_ccl_get(ctx->ccls->ccls, trans.ccl); + + if ( ! ccl_is_epsilon(ccl) ) { + // Keep transition.n + jrx_nfa_transition t = {trans.ccl, trans.succ, + trans.tags ? set_tag_copy(trans.tags) : 0}; + vec_nfa_transition_append(ntrans, t); + } + + else { + // Collect all states (plus all tags/accepts/assertions_ + // along the way) that we can reach by epsilon transitions + // only. + set_nfa_state_id* closure = set_nfa_state_id_create(0); + set_tag* tags = trans.tags ? set_tag_copy(trans.tags) : 0; + + jrx_nfa_state* succ = vec_nfa_state_get(ctx->states, trans.succ); + _nfa_state_follow_epsilons(ctx, succ, closure, ntrans, &tags, &state->accepts, + ccl->assertions); + + set_nfa_state_id_delete(closure); + + if ( tags ) { + if ( state == nfa->initial ) { + if ( ! nfa->initial_tags ) + nfa->initial_tags = set_tag_copy(tags); + else + set_tag_join(nfa->initial_tags, tags); + } + + set_tag_delete(tags); + } + } + } + + vec_for_each(nfa_transition, state->trans, t) + { + if ( t.tags ) + set_tag_delete(t.tags); + } + + vec_nfa_transition_delete(state->trans); + + state->trans = ntrans; + } +} + +static jrx_nfa* _nfa_compile_pattern(jrx_nfa_context* ctx, const char* pattern, int len, + const char** errmsg) +{ + yyscan_t scanner; + jrx_nfa* nfa = 0; + + RE_lex_init(&scanner); + // FIXME: This assumes that there aren't null bytes in there ... + RE__scan_bytes(pattern, len, scanner); + + const char* internal_errmsg = 0; + + RE_set_extra(&internal_errmsg, scanner); + + int i = RE_parse(scanner, ctx, &nfa); + + RE_lex_destroy(scanner); + + if ( i == 1 && ! internal_errmsg ) + internal_errmsg = "parser error"; + + if ( i == 2 ) + internal_errmsg = "out of memory during parsing"; + + if ( internal_errmsg ) { + nfa_context_delete(ctx); + ctx = 0; + + if ( errmsg ) + *errmsg = internal_errmsg; + + return 0; + } + + assert(nfa); + + // We take the next available accept IDs if we don't have one set yet. + if ( ! nfa->final->accepts ) + nfa = nfa_set_accept(nfa, ++ctx->max_accept); + + if ( ctx->options & JRX_OPTION_DEBUG ) + nfa_print(nfa, stderr); + + nfa_remove_epsilons(nfa); + + if ( ctx->options & JRX_OPTION_DEBUG ) + nfa_print(nfa, stderr); + + return nfa; +} + +jrx_nfa* nfa_compile_add(jrx_nfa* nfa, const char* pattern, int len, const char** errmsg) +{ +#if 0 + if ( ! (nfa->ctx->options & JRX_OPTION_NO_CAPTURE) ) { + *errmsg = "cannot capture subgroups with set matching; use OPTION_NO_CAPTURE"; + nfa_delete(nfa); + return 0; + } +#endif + + jrx_nfa* nnfa = _nfa_compile_pattern(nfa->ctx, pattern, len, errmsg); + if ( ! nnfa ) { + nfa_delete(nfa); + return 0; + } + + return nfa_alternative(nfa, nnfa); +} + +jrx_nfa* nfa_compile(const char* pattern, int len, jrx_option options, int8_t nmatch, + const char** errmsg) +{ + if ( options & JRX_OPTION_NO_CAPTURE ) + nmatch = 0; + + jrx_nfa_context* ctx = nfa_context_create(options, nmatch); + return _nfa_compile_pattern(ctx, pattern, len, errmsg); +} + +static void _set_tag_print(set_tag* tags, FILE* file) +{ + if ( ! tags ) { + fputs("none", file); + return; + } + + int first = 1; + set_for_each(tag, tags, tag) + { + if ( ! first ) + fputs(",", file); + fprintf(file, "%d@%d", tag.reg, tag.prio); + first = 0; + } +} + +void nfa_state_print(jrx_nfa_context* ctx, jrx_nfa_state* state, FILE* file) +{ + fprintf(file, "state %d\n", state->id); + + if ( state->accepts ) { + fprintf(file, " accepts with"); + vec_for_each(nfa_accept, state->accepts, acc) + { + fprintf(file, " %d, tags", acc.aid); + _set_tag_print(acc.tags, file); + fprintf(file, ", final assertions %d", acc.assertions); + } + + fprintf(file, "\n"); + } + + vec_for_each(nfa_transition, state->trans, trans) + { + ccl_print(vec_ccl_get(ctx->ccls->ccls, trans.ccl), file); + fprintf(file, " -> %d ", trans.succ); + fputs("(tags ", file); + _set_tag_print(trans.tags, file); + fputs(")", file); + fputs("\n", file); + } +} + +void nfa_print(jrx_nfa* nfa, FILE* file) +{ + // We compute a closure to print only relevant states. + set_nfa_state_id* closure = set_nfa_state_id_create(0); + _nfa_state_closure(nfa->ctx, nfa->initial, closure); + + fprintf(file, "== NFA with %d used states\n", set_nfa_state_id_size(closure)); + + if ( nfa->initial_tags ) { + fprintf(stderr, " initial tags "); + _set_tag_print(nfa->initial_tags, file); + fprintf(stderr, "\n"); + } + + set_for_each(nfa_state_id, closure, nid) + { + jrx_nfa_state* state = vec_nfa_state_get(nfa->ctx->states, nid); + assert(state); + + nfa_state_print(nfa->ctx, state, file); + + if ( state == nfa->initial ) + fputs(" -> initial state\n", file); + + if ( state == nfa->final ) + fputs(" -> final state\n", file); + + fputc('\n', file); + } + + set_nfa_state_id_delete(closure); + + if ( ! nfa->ctx->ccls ) + return; + + fputs("CCLs:\n", file); + ccl_group_print(nfa->ctx->ccls, file); + fputs("\n", file); +} + +//@} diff --git a/hilti/src/3rdparty/justrx/src/nfa.h b/hilti/src/3rdparty/justrx/src/nfa.h new file mode 100644 index 000000000..89e4affa5 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/nfa.h @@ -0,0 +1,148 @@ +// $Id$ + +/// \addtogroup NFA +/// +/// Functions for manipulating NFAs. + +#ifndef JRX_NFA_H +#define JRX_NFA_H + +#include "ccl.h" +#include "jrx-intern.h" + +/** \addtogroup NFA */ +// @{ + +/// Defines a tag by register ID and priority. Tags can be attached to +/// transitions and will then during matching assign the current input +/// position to the tag's register. If multiple transitions reach the same +/// destination state simultaneously while attempting to set different +/// registers, the tag with highest priority will win. +typedef struct { + int8_t reg; ///< Tag's register. + int8_t prio; ///< Tag's Priority. Default is zero, and larger priority is more important. +} jrx_tag; + +/// A set of ~~nfa_state_id. +DECLARE_SET(nfa_state_id, jrx_nfa_state_id, jrx_nfa_state_id, SET_STD_EQUAL) + +static inline int _jrx_cmp_tag(jrx_tag t1, jrx_tag t2) +{ + return t1.reg != t2.reg ? SET_STD_EQUAL(t1.reg, t2.reg) : SET_STD_EQUAL(t1.prio, t2.prio); +} + +/// A ~~set of ~~jrx_tag. +DECLARE_SET(tag, jrx_tag, uint32_t, _jrx_cmp_tag) + +struct jrx_nfa_state; + +/// A ~~vector of ~~jrx_nfa_state pointers. +DECLARE_VECTOR(nfa_state, struct jrx_nfa_state*, jrx_nfa_state_id) + +/// Groups a set of related NFAs together. NFA which are manipulated jointly +/// (e.g., by building a new NFA out of a set of others) must be part of the +/// same context. Each NFA only exists as long as the context is valid which +/// it is part of. +typedef struct { + jrx_option options; // Options applying to all NFAs. + int8_t nmatch; // Max. number of captures the user is interested in. + int8_t max_tag; // Largest tag number used. + int8_t max_capture; // Largest capture group number used. + jrx_accept_id max_accept; // Highest accept ID assigned so far. + jrx_ccl_group* ccls; // All CCLs. + vec_nfa_state* states; // Vector of states indexed by their ID. + int refcnt; // Reference counter for memory management. +} jrx_nfa_context; + + +/// A transition between two NFA states. +typedef struct { + jrx_ccl_id ccl; // CCL for transition. + jrx_nfa_state_id succ; // Successor state. + set_tag* tags; // Tags to apply on transition. +} jrx_nfa_transition; + +/// Attached to an NFA state to signal acceptance. +typedef struct { + jrx_assertion assertions; // Final assertions needed for acceptance. + jrx_accept_id aid; // Accept with this ID. + set_tag* tags; // Final tags to apply when accepting. +} jrx_nfa_accept; + +/// A vector of ~~nfa_accept. +DECLARE_VECTOR(nfa_accept, jrx_nfa_accept, uint32_t) + +/// A vector of ~~nfa_transition. +DECLARE_VECTOR(nfa_transition, jrx_nfa_transition, uint32_t) + +/// An individual NFA state. +typedef struct jrx_nfa_state { + jrx_nfa_state_id id; // Unique ID for this state. + vec_nfa_accept* accepts; // Accept with these, or 0 if none. + vec_nfa_transition* trans; // Pointer to transition array. +} jrx_nfa_state; + +/// An NFA. Each NFA is associated with an ~~jrx_nfa_context. +typedef struct jrx_nfa { + jrx_nfa_context* ctx; // The context the NFA is part of. + set_tag* initial_tags; // The "incoming" tags. + jrx_nfa_state* initial; // The initial state. + jrx_nfa_state* final; // The final state. +} jrx_nfa; + +/// Creates a new NFA context. +/// +/// \param options Options applying to all NFAs associated with this context. +/// +/// \param nmatch The maximum number of capture groups one is interested in for any +/// NFA associated with this context; -1 if access to *all* groups is desired. +extern jrx_nfa_context* nfa_context_create(jrx_option options, int8_t nmatch); + + +/// Delete an NFA context. +/// +/// \param ctx The context to delete. The instance it points must not be +/// accessed anymore after the call. +extern void nfa_context_delete(jrx_nfa_context* ctx); + +/// Creates a new NFA state. +/// +/// \param ctx The context the NFA is to be associated with. +/// \param initial The initial state of the NFA. +/// \param final The final state of the NFA. +/// +/// Note: The *final* state will be used for link this NFA with others, like +/// with ~~nfa_concat. We assume there's only a single state representing the +/// "exit" position. The final state does not need to be an accepting state. +extern jrx_nfa* nfa_create(jrx_nfa_context* ctx, jrx_nfa_state* initial, jrx_nfa_state* final); + +/// Delete an NFA. +/// +/// \param nfa The NFA to delete. The instance it points must not be accessed +/// anymore after the call. +extern void nfa_delete(jrx_nfa* nfa); + +extern jrx_nfa* nfa_set_accept(jrx_nfa* nfa, jrx_accept_id accept); +extern jrx_nfa* nfa_set_capture(jrx_nfa* nfa, uint8_t group); + +extern jrx_nfa* nfa_empty(jrx_nfa_context* ctx); +extern jrx_nfa* nfa_from_ccl(jrx_nfa_context* ctx, jrx_ccl* ccl); +extern jrx_nfa* nfa_concat(jrx_nfa* nfa1, jrx_nfa* nfa2, jrx_ccl* ccl); +extern jrx_nfa* nfa_alternative(jrx_nfa* nfa1, jrx_nfa* nfa2); +extern jrx_nfa* nfa_iterate(jrx_nfa* nfa, int min, int max); + +extern void nfa_remove_epsilons(jrx_nfa* nfa); + +// Compile a single pattern. +extern jrx_nfa* nfa_compile(const char* pattern, int len, jrx_option options, int8_t nmatch, + const char** errmsg); + +// Add another pattern alternative to an existing NFA. +extern jrx_nfa* nfa_compile_add(jrx_nfa* nfa, const char* pattern, int len, const char** errmsg); + +extern void nfa_state_print(jrx_nfa_context* ctx, jrx_nfa_state* state, FILE* file); +extern void nfa_print(jrx_nfa* nfa, FILE* file); + +//@} + +#endif diff --git a/hilti/src/3rdparty/justrx/src/re-parse.y b/hilti/src/3rdparty/justrx/src/re-parse.y new file mode 100644 index 000000000..9342e5bcc --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/re-parse.y @@ -0,0 +1,173 @@ +%{ + +#ifndef __clang_analyzer__ + +#include +#include + +#include + +extern void RE_error(void* scanner, jrx_nfa_context* nfactx, jrx_nfa** nfa, const char* msg); +extern int RE_lex(void* yylval_param, void* yyscanner); + +#define parse_error(msg) RE_error(scanner, 0, 0, msg) + +%} + +%define api.prefix {RE_} +%code provides { #define YYSTYPE RE_STYPE } + +%define api.pure +%file-prefix "re-parse" +%defines + +%lex-param{void* scanner} +%parse-param{void* scanner} +%parse-param{jrx_nfa_context* nfactx} +%parse-param{jrx_nfa** nfa} + +%token TOK_ASSERTION TOK_CODEPOINT TOK_NEGATE_CCL TOK_COUNT TOK_DYNCCL TOK_ACCEPT_ID + +%union { + jrx_char cp; + jrx_assertion assertion; + jrx_std_ccl dynccl; + int count; + + jrx_nfa* nfa; + jrx_ccl* ccl; +} + +%type TOK_CODEPOINT +%type TOK_ASSERTION assertions +%type TOK_DYNCCL +%type TOK_COUNT TOK_ACCEPT_ID opt_count opt_accept_id + +%type ccl ccl_elem +%type complete_regexp regexp alternatives singletons singleton + +%% + +complete_regexp : regexp opt_accept_id + { + *nfa = $1; + *nfa = nfa_set_capture(*nfa, 0); + + /* Add a .* if requested. */ + if ( nfactx->options & JRX_OPTION_DONT_ANCHOR ) { + jrx_nfa* any = nfa_from_ccl(nfactx, ccl_any(nfactx->ccls)); + *nfa = nfa_concat(nfa_iterate(any, 0, -1), *nfa, 0); + } + + if ( $2 > 0 ) + *nfa = nfa_set_accept(*nfa, $2); + } + ; + +regexp : alternatives + { $$ = $1; } + ; + +opt_accept_id : TOK_ACCEPT_ID + { $$ = $1; } + | + { $$ = -1; } + ; + +alternatives : singletons '|' alternatives + { $$ = nfa_alternative($1, $3); } + | singletons + { $$ = $1; } + ; + +singletons : singleton singletons + { $$ = nfa_concat($1, $2, 0); } + | singleton assertions singletons + { + jrx_ccl* ccl = ccl_epsilon(nfactx->ccls); + ccl = ccl_add_assertions(ccl, $2); + $$ = nfa_concat($1, $3, ccl); + } + + | singleton + { $$ = $1; } + ; + +assertions : TOK_ASSERTION + { $$ = $1; } + + | TOK_ASSERTION assertions + { $$ = ($1 | $2); } + +singleton : singleton '*' + { $$ = nfa_iterate($1, 0, -1); } + + | singleton '+' + { $$ = nfa_iterate($1, 1, -1); } + + | singleton '?' + { $$ = nfa_iterate($1, 0, 1); } + + | singleton '{' opt_count ',' opt_count '}' + { + if ( $3 > $5 && $5 >= 0 ) + parse_error("bad interation value"); + else + $$ = nfa_iterate($1, $3, $5); + } + + | singleton '{' TOK_COUNT '}' + { + if ( $3 < 0 ) + parse_error("bad interation value"); + else + $$ = nfa_iterate($1, $3, $3); + } + + | '.' + { $$ = nfa_from_ccl(nfactx, ccl_any(nfactx->ccls)); } + + | '[' ccl ']' + { $$ = nfa_from_ccl(nfactx, $2); } + + | '[' TOK_NEGATE_CCL ccl ']' + { $$ = nfa_from_ccl(nfactx, ccl_negate($3)); } + + | '(' + { $$ = ++nfactx->max_capture; } + + regexp ')' + { $$ = nfa_set_capture($3, $2); } + + | TOK_CODEPOINT + { $$ = nfa_from_ccl(nfactx, ccl_from_range(nfactx->ccls, $1, $1 + 1)); } + + | + { $$ = nfa_empty(nfactx); } + ; + +opt_count : TOK_COUNT + { $$ = $1; } + | + { $$ = -1; } + +ccl : ccl_elem ccl + { $$ = ccl_join($1, $2); } + | ccl_elem + { $$ = $1; } + + +ccl_elem : TOK_CODEPOINT '-' TOK_CODEPOINT + { $$ = ccl_from_range(nfactx->ccls, $1, $3 + 1); } + + | TOK_DYNCCL + { $$ = ccl_from_std_ccl(nfactx->ccls, $1); } + + | TOK_CODEPOINT + { $$ = ccl_from_range(nfactx->ccls, $1, $1 + 1); } + ; + + +%% + +#endif diff --git a/hilti/src/3rdparty/justrx/src/re-scan.l b/hilti/src/3rdparty/justrx/src/re-scan.l new file mode 100644 index 000000000..5b89332ea --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/re-scan.l @@ -0,0 +1,92 @@ + +/* Loosely based on Bro's re-scan.l */ + +%{ + +#ifndef __clang_analyzer__ + +#include +#include +#include +#include + +#define RE_error RE_error +void RE_error(yyscan_t scanner, const char* msg); + +%} + +%x COUNT CCL_FIRST CCL ACCEPT_ID +%option nounput noyywrap reentrant bison-bridge prefix="RE_" + +ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]{2})) + +CCL_EXPR ("[:"[[:alpha:]]+":]") +CCL_CHAR_FIRST ([^\\]|{CCL_EXPR}|{ESCSEQ}) +CCL_CHAR ([^\\\]]|{CCL_EXPR}|{ESCSEQ}) + +ACCEPT_ID \{#[[:digit:]]+\} + +%% + +{ + {ACCEPT_ID} yylval->count = atoi(yytext+2); return TOK_ACCEPT_ID; + + "{"/[[:digit:]] BEGIN(COUNT); return '{'; + "[" BEGIN(CCL_FIRST); return '['; + + [|*+?.(){}] return yytext[0]; + + "^" yylval->assertion = JRX_ASSERTION_BOL; return TOK_ASSERTION; + "$" yylval->assertion = JRX_ASSERTION_EOL; return TOK_ASSERTION; + "\\b" yylval->assertion = JRX_ASSERTION_WORD_BOUNDARY; return TOK_ASSERTION; + "\\B" yylval->assertion = JRX_ASSERTION_NOT_WORD_BOUNDARY; return TOK_ASSERTION; + + . yylval->cp = yytext[0]; return TOK_CODEPOINT; +} + +{ + "^" return TOK_NEGATE_CCL; + . BEGIN(CCL); yylval->cp = yytext[0]; return TOK_CODEPOINT; +} + +{ + -/[^\]] return '-'; + [^\]] yylval->cp = yytext[0]; return TOK_CODEPOINT; + "]" BEGIN(INITIAL); return ']'; +} + +{ + "[:lower:]" BEGIN(CCL); yylval->dynccl = JRX_STD_CCL_LOWER; return TOK_DYNCCL; + "[:upper:]" BEGIN(CCL); yylval->dynccl = JRX_STD_CCL_UPPER; return TOK_DYNCCL; + "[:digit:]" BEGIN(CCL); yylval->dynccl = JRX_STD_CCL_DIGIT; return TOK_DYNCCL; + "[:blank:]" BEGIN(CCL); yylval->dynccl = JRX_STD_CCL_BLANK; return TOK_DYNCCL; + {CCL_EXPR} RE_error(yyscanner, "bad character class"); BEGIN(CCL); return TOK_DYNCCL; +} + +{ + [[:digit:]]+ yylval->count = atoi(yytext); return TOK_COUNT; + "," return ','; + "}" BEGIN(INITIAL); return '}'; + . RE_error(yyscanner, "bad character inside {}'s"); BEGIN(INITIAL); return '}'; +} + +{ESCSEQ} { + const char* esc_text = yytext + 1; + yylval->cp = jrx_expand_escape(esc_text); + + if ( YY_START == CCL_FIRST ) + BEGIN(CCL); + + return TOK_CODEPOINT; + } + +%% + +void RE_error(yyscan_t scanner, const char* msg) +{ + const char** errmsg = RE_get_extra(scanner); + if ( errmsg && ! *errmsg ) + *errmsg = msg; +} + +#endif diff --git a/hilti/src/3rdparty/justrx/src/regex.h b/hilti/src/3rdparty/justrx/src/regex.h new file mode 100644 index 000000000..86a168091 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/regex.h @@ -0,0 +1,17 @@ +// $Id$ +// +// regcomp/regexec compatible interface, as far as our capabilities allow. + +#ifndef JRX_REGEX_H +#define JRX_REGEX_H + +#include "jrx.h" + +#define regcomp jrx_regcomp +#define regerror jrx_regerror +#define regexec jrx_regexec +#define regfree jrx_regfree +#define regex_t jrx_regex_t +#define regmatch_t jrx_regmatch_t + +#endif diff --git a/hilti/src/3rdparty/justrx/src/set.h b/hilti/src/3rdparty/justrx/src/set.h new file mode 100644 index 000000000..2a64f2196 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/set.h @@ -0,0 +1,274 @@ +// $Id$ +// +// Simple sets of fixed-sized value types (which must be value-copyable). +// +// TODO: We should use a heap structure rather than a bubbling at insertion. + +#ifndef JRX_SET_H +#define JRX_SET_H + +#include +#include +#include + +static const int SET_DEFAULT_SIZE = 2; +static const double SET_GROWTH_FACTOR = 1.5; + +#define SET_STD_EQUAL(a, b) (a < b ? -1 : (a == b ? 0 : 1)) + +// #macro-start +#define DECLARE_SET(name, set_elem_t, set_size_t, cmp_func) \ + \ + struct set_##name { \ + set_size_t size; /* Current number of elements */ \ + set_size_t max; /* Maximum number of elements we have space for. */ \ + set_elem_t* elems; /* Elements themselves. */ \ + }; \ + \ + struct frozen_set_##name { \ + set_size_t size; /* Number of elements */ \ + set_elem_t elems[]; /* Elements themselves. */ \ + }; \ + \ + typedef struct set_##name set_##name; \ + typedef struct frozen_set_##name frozen_set_##name; \ + \ + typedef set_elem_t set_##name##_elem_t; \ + typedef set_size_t set_##name##_size_t; \ + \ + static inline set_size_t min_##name(set_size_t a, set_size_t b) \ + { \ + return a < b ? a : b; \ + } \ + \ + static inline int set_##name##_resize(set_##name* set, set_size_t nsize) \ + { \ + if ( nsize < SET_DEFAULT_SIZE ) \ + nsize = SET_DEFAULT_SIZE; \ + set->elems = realloc(set->elems, nsize * sizeof(set_elem_t)); \ + if ( ! set->elems ) \ + return 0; \ + \ + set->max = nsize; \ + \ + if ( set->size > nsize ) \ + set->size = nsize; \ + \ + return 1; \ + } \ + \ + static inline set_##name* set_##name##_create(set_size_t size) \ + { \ + set_size_t max = size ? size : SET_DEFAULT_SIZE; \ + \ + set_##name* set = (set_##name*)malloc(sizeof(set_##name)); \ + if ( ! set ) \ + return 0; \ + \ + set->elems = (set_elem_t*)malloc(max * sizeof(set_elem_t)); \ + if ( ! set->elems ) { \ + free(set); \ + return 0; \ + } \ + \ + set->size = 0; \ + set->max = max; \ + return set; \ + } \ + \ + static inline void set_##name##_delete(set_##name* set) \ + { \ + if ( set ) { \ + if ( set->elems ) \ + free(set->elems); \ + free(set); \ + } \ + } \ + \ + static inline set_##name* set_##name##_copy(set_##name* set) \ + { \ + set_##name* copy = set_##name##_create(set->max); \ + if ( ! copy ) \ + return 0; \ + \ + assert(set->elems); \ + memcpy(copy->elems, set->elems, set->size * sizeof(set_elem_t)); \ + copy->size = set->size; \ + return copy; \ + } \ + \ + static inline int set_##name##_empty(set_##name* set) \ + { \ + return set->size == 0; \ + } \ + \ + /* Public interface: Returns non-zero if found. */ \ + /* Private interface: Returns index+1 if found. */ \ + static inline set_size_t set_##name##_contains(set_##name* set, set_elem_t elem) \ + { \ + assert(set); \ + if ( ! set->size ) \ + return 0; \ + \ + set_size_t min = 0; \ + set_size_t max = set->size - 1; \ + \ + while ( min <= max ) { \ + set_size_t m = (min + max) / 2; \ + \ + int cmp = cmp_func(set->elems[m], elem); \ + \ + if ( cmp == 0 ) \ + return m + 1; \ + \ + if ( cmp < 0 ) \ + min = m + 1; \ + else if ( ! m ) \ + break; \ + else \ + max = m - 1; \ + } \ + \ + return 0; \ + } \ + \ + static inline set_size_t set_##name##_size(set_##name* set) \ + { \ + assert(set); \ + return set->size; \ + } \ + \ + static inline int set_##name##_insert(set_##name* set, set_elem_t elem) \ + { \ + if ( set_##name##_contains(set, elem) ) \ + return 1; \ + \ + if ( set->size + 1 > set->max ) { \ + if ( ! set_##name##_resize(set, (set_size_t)(set->size * SET_GROWTH_FACTOR)) ) \ + return 0; \ + } \ + \ + assert(set); \ + assert(set->elems); \ + set->elems[set->size] = elem; \ + set->size++; \ + \ + /* Bubble it to the right place */ \ + set_size_t i; \ + for ( i = set->size - 1; i > 0; i-- ) { \ + if ( cmp_func(set->elems[i], set->elems[i - 1]) >= 0 ) \ + break; \ + \ + set_elem_t tmp = set->elems[i]; \ + set->elems[i] = set->elems[i - 1]; \ + set->elems[i - 1] = tmp; \ + } \ + return 1; \ + } \ + \ + static inline int set_##name##_remove(set_##name* set, set_elem_t elem) \ + { \ + set_size_t idx = set_##name##_contains(set, elem); \ + if ( ! idx ) \ + return 1; \ + \ + if ( idx < set->size ) \ + memcpy(set->elems + idx - 1, set->elems + idx, \ + (set->size - idx) * sizeof(set_elem_t)); \ + \ + set->size--; \ + \ + set_size_t gsize = (set_size_t)(set->size / SET_GROWTH_FACTOR); \ + \ + return gsize && set->size >= gsize ? 1 : set_##name##_resize(set, gsize); \ + } \ + \ + static inline int set_##name##_equal(set_##name* s1, set_##name* s2) \ + { \ + if ( s1->size != s2->size ) \ + return 0; \ + \ + set_size_t i; \ + for ( i = 0; i < s1->size; i++ ) { \ + if ( cmp_func(s1->elems[i], s2->elems[i]) != 0 ) \ + return 0; \ + } \ + \ + return 1; \ + } \ + \ + static inline int set_##name##_join(set_##name* set, const set_##name* other) \ + { \ + set_size_t i; \ + for ( i = 0; i < other->size; i++ ) { \ + if ( ! set_##name##_insert(set, other->elems[i]) ) \ + return 0; \ + } \ + \ + return 1; \ + } \ + \ + static inline set_size_t set_##name##_begin(set_##name* set) \ + { \ + return 0; \ + } \ + \ + static inline set_size_t set_##name##_end(set_##name* set) \ + { \ + return set->size; \ + } \ + \ + static inline frozen_set_##name* set_##name##_freeze(set_##name* set) \ + { \ + size_t size = sizeof(frozen_set_##name) + set->size * sizeof(set_elem_t); \ + frozen_set_##name* fset = (frozen_set_##name*)malloc(size); \ + if ( ! set ) \ + return 0; \ + \ + fset->size = set->size; \ + memcpy(fset->elems, set, set->size * sizeof(set_elem_t)); \ + set_##name##_delete(set); \ + return fset; \ + } \ + \ + static inline void frozen_set_##name##_delete(frozen_set_##name* set) \ + { \ + free(set); \ + } \ + \ + static inline set_size_t frozen_set_##name##_size(frozen_set_##name* set) \ + { \ + return set->size; \ + } \ + \ + static inline set_elem_t frozen_set_##name##_index(frozen_set_##name* set, set_size_t idx) \ + { \ + return set->elems[idx]; \ + } \ + \ + static inline int set_##name##_iter_equal(set_size_t iter1, set_size_t iter2) \ + { \ + return iter1 == iter2; \ + } \ + \ + static inline set_size_t set_##name##_iter_next(set_size_t iter) \ + { \ + return iter + 1; \ + } \ + \ + static inline set_elem_t set_##name##_iter_deref(set_##name* set, set_size_t iter) \ + { \ + return set->elems[iter]; \ + } \ + \ +// #macro-end + +#define set_for_each(name, set, var) \ + assert((set)->elems); \ + set_##name##_elem_t var; \ + set_##name##_size_t __i##var; \ + if ( (set)->size ) \ + var = (set)->elems[0]; \ + for ( __i##var = 0; __i##var < (set)->size; \ + __i##var++, var = (set)->elems[__i##var < (set)->size ? __i##var : 0] ) +#endif diff --git a/hilti/src/3rdparty/justrx/src/util.c b/hilti/src/3rdparty/justrx/src/util.c new file mode 100644 index 000000000..1137ec295 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/util.c @@ -0,0 +1,88 @@ +// $Id$ + +#include "jrx-intern.h" + +#include +#include +#include + +// Copied and adapted from Bro. +jrx_char jrx_expand_escape(const char* s) +{ + switch ( *(s++) ) { + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'a': + return '\a'; + case 'v': + return '\v'; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + // \{1,3} + --s; // put back the first octal digit + const char* start = s; + + // Don't increment inside loop control + // because if isdigit() is a macro it might + // expand into multiple increments ... + // + // Here we define a maximum length for escape sequence + // to allow easy handling of string like: "^H0" as + // "\0100". + // + int len; + for ( len = 0; len < 3 && isascii(*s) && isdigit(*s); ++s, ++len ) + ; + + int result; + if ( sscanf(start, "%3o", &result) != 1 ) { + // warn("bad octal escape: ", start); + result = 0; + } + + return result; + } + + case 'x': { + /* \x */ + const char* start = s; + + // Look at most 2 characters, so that "\x0ddir" -> "^Mdir". + int len; + for ( len = 0; len < 2 && isascii(*s) && isxdigit(*s); ++s, ++len ) + ; + + int result; + if ( sscanf(start, "%2x", &result) != 1 ) { + // warn("bad hexadecimal escape: ", start); + result = 0; + } + + return result; + } + + default: + return s[-1]; + } +} + +void jrx_internal_error(const char* msg) +{ + fprintf(stderr, "jitre internal error: %s", msg); + abort(); +} diff --git a/hilti/src/3rdparty/justrx/src/util.h b/hilti/src/3rdparty/justrx/src/util.h new file mode 100644 index 000000000..e33bd9bac --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/util.h @@ -0,0 +1,11 @@ +// $Id$ + +#ifndef JRX_UTIL_H +#define JRX_UTIL_H + +#include "jrx-intern.h" + +extern jrx_char jrx_expand_escape(const char* s); +extern void jrx_internal_error(const char* msg); + +#endif diff --git a/hilti/src/3rdparty/justrx/src/vector.h b/hilti/src/3rdparty/justrx/src/vector.h new file mode 100644 index 000000000..f46d792d0 --- /dev/null +++ b/hilti/src/3rdparty/justrx/src/vector.h @@ -0,0 +1,145 @@ +// $Id$ +// +// Simple auto-growing vectors. The value of elements not yet initialized is +// set to zero. + +#ifndef JRX_VECTOR_H +#define JRX_VECTOR_H + +#include + +static const int VECTOR_DEFAULT_SIZE = 2; +static const double VECTOR_GROWTH_FACTOR = 1.5; + +// #macro-start +#define DECLARE_VECTOR(name, vec_elem_t, vec_size_t) \ + \ + typedef vec_elem_t vec_##name##_elem_t; \ + typedef vec_size_t vec_##name##_size_t; \ + \ + typedef struct vec_##name { \ + vec_size_t size; /* Largest index + 1 written to so far. */ \ + vec_size_t max; /* Number of slots allocated. */ \ + vec_elem_t* elems; /* Elements themselves. */ \ + } vec_##name; \ + \ + static inline int vec_##name##_resize(vec_##name* vec, vec_size_t nmax) \ + { \ + if ( nmax < VECTOR_DEFAULT_SIZE ) \ + nmax = VECTOR_DEFAULT_SIZE; \ + \ + vec->elems = (vec_elem_t*)realloc(vec->elems, nmax * sizeof(vec_elem_t)); \ + if ( ! vec->elems ) \ + return 0; \ + \ + if ( nmax > vec->max ) \ + memset(&vec->elems[vec->max], 0, sizeof(vec_elem_t) * (nmax - vec->max)); \ + \ + vec->max = nmax; \ + return 1; \ + } \ + \ + static inline vec_##name* vec_##name##_create(vec_size_t size) \ + { \ + vec_size_t max = size ? size : VECTOR_DEFAULT_SIZE; \ + \ + vec_##name* vec = (vec_##name*)malloc(sizeof(vec_##name)); \ + if ( ! vec ) \ + return 0; \ + \ + vec->elems = (vec_elem_t*)calloc(max, sizeof(vec_elem_t)); \ + if ( ! vec->elems ) { \ + free(vec); \ + return 0; \ + } \ + \ + vec->max = max; \ + vec->size = 0; \ + return vec; \ + } \ + \ + static inline void vec_##name##_delete(vec_##name* vec) \ + { \ + free(vec->elems); \ + free(vec); \ + } \ + \ + static inline vec_##name* vec_##name##_copy(vec_##name* vec) \ + { \ + vec_##name* copy = vec_##name##_create(vec->max); \ + if ( ! copy ) \ + return 0; \ + \ + memcpy(copy->elems, vec->elems, vec->max * sizeof(vec_elem_t)); \ + copy->max = vec->max; \ + return copy; \ + } \ + \ + static inline vec_elem_t* vec_##name##_freeze(vec_##name* vec) \ + { \ + vec_elem_t* elems = vec->elems; \ + free(vec); \ + return elems; \ + } \ + \ + static inline vec_size_t vec_##name##_size(vec_##name* vec) \ + { \ + return vec->size; \ + } \ + \ + static inline int vec_##name##_set(vec_##name* vec, vec_size_t idx, vec_elem_t elem) \ + { \ + if ( idx >= vec->max ) { \ + int nmax = vec->max; \ + do { \ + nmax *= VECTOR_GROWTH_FACTOR; \ + } while ( idx >= nmax ); \ + \ + if ( ! vec_##name##_resize(vec, nmax) ) \ + return 0; \ + } \ + \ + assert(idx < vec->max); \ + \ + vec->elems[idx] = elem; \ + if ( idx >= vec->size ) \ + vec->size = idx + 1; \ + return 1; \ + } \ + \ + \ + static inline vec_size_t vec_##name##_append(vec_##name* vec, vec_elem_t elem) \ + { \ + assert(vec); \ + vec_size_t idx = vec->size; \ + vec_##name##_set(vec, idx, elem); \ + return idx; \ + } \ + \ + static inline vec_elem_t vec_##name##_get(vec_##name* vec, vec_size_t idx) \ + { \ + assert(vec); \ + assert(vec->elems); \ + if ( idx >= vec->max ) { \ + vec_elem_t zero; \ + memset(&zero, 0, sizeof(zero)); \ + return zero; \ + } \ + \ + return vec->elems[idx]; \ + } \ + \ +// #macro-end + +#define vec_for_each(name, vec, var) \ + vec_##name##_elem_t var; \ + vec_##name##_size_t __j##var; \ + if ( (vec)->size ) \ + var = (vec)->elems[0]; \ + else \ + bzero(&var, sizeof(var)); \ + for ( __j##var = 0; __j##var < (vec)->size; \ + __j##var++, var = (vec)->elems[__j##var < (vec)->size ? __j##var : 0] ) + + +#endif diff --git a/hilti/src/3rdparty/justrx/test/CMakeLists.txt b/hilti/src/3rdparty/justrx/test/CMakeLists.txt new file mode 100644 index 000000000..e37b10834 --- /dev/null +++ b/hilti/src/3rdparty/justrx/test/CMakeLists.txt @@ -0,0 +1,8 @@ + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) + +add_executable(retest retest.c) +target_link_libraries(retest jrx) + +add_executable(testregex testregex.c) +target_link_libraries(testregex jrx) diff --git a/hilti/src/3rdparty/justrx/test/Makefile b/hilti/src/3rdparty/justrx/test/Makefile new file mode 100644 index 000000000..bbf87552e --- /dev/null +++ b/hilti/src/3rdparty/justrx/test/Makefile @@ -0,0 +1,4 @@ +# $Id$ + +all: + ( cd .. && make ) diff --git a/hilti/src/3rdparty/justrx/test/basic.dat b/hilti/src/3rdparty/justrx/test/basic.dat new file mode 100644 index 000000000..c956c451b --- /dev/null +++ b/hilti/src/3rdparty/justrx/test/basic.dat @@ -0,0 +1,215 @@ +NOTE all standard compliant implementations should pass these : 2002-05-31 + +BE abracadabra$ abracadabracadabra (7,18) +BE a...b abababbb (2,7) +BE XXXXXX ..XXXXXX (2,8) +E \) () (1,2) +BE a] a]a (0,2) +B } } (0,1) +E \} } (0,1) +BE \] ] (0,1) +B ] ] (0,1) +E ] ] (0,1) +B { { (0,1) +B } } (0,1) +BE ^a ax (0,1) +BE \^a a^a (1,3) +BE a\^ a^ (0,2) +BE a$ aa (1,2) +BE a\$ a$ (0,2) +BE ^$ NULL (0,0) +E $^ NULL (0,0) +E a($) aa (1,2)(2,2) +E a*(^a) aa (0,1)(0,1) +E (..)*(...)* a (0,0) +E (..)*(...)* abcd (0,4)(2,4) +E (ab|a)(bc|c) abc (0,3)(0,2)(2,3) +E (ab)c|abc abc (0,3)(0,2) +E a{0}b ab (1,2) +E (a*)(b?)(b+)b{3} aaabbbbbbb (0,10)(0,3)(3,4)(4,7) +E (a*)(b{0,1})(b{1,})b{3} aaabbbbbbb (0,10)(0,3)(3,4)(4,7) +E ((a|a)|a) a (0,1)(0,1)(0,1) +E (a*)(a|aa) aaaa (0,4)(0,3)(3,4) +E a*(a.|aa) aaaa (0,4)(2,4) +E a(b)|c(d)|a(e)f aef (0,3)(?,?)(?,?)(1,2) +E (a|b)?.* b (0,1)(0,1) +E (a|b)c|a(b|c) ac (0,2)(0,1) +E (a|b)c|a(b|c) ab (0,2)(?,?)(1,2) +E (a|b)*c|(a|ab)*c abc (0,3)(1,2) +E (a|b)*c|(a|ab)*c xc (1,2) +E (.a|.b).*|.*(.a|.b) xa (0,2)(0,2) +E a?(ab|ba)ab abab (0,4)(0,2) +E a?(ac{0}b|ba)ab abab (0,4)(0,2) +E ab|abab abbabab (0,2) +E aba|bab|bba baaabbbaba (5,8) +E aba|bab baaabbbaba (6,9) +E (aa|aaa)*|(a|aaaaa) aa (0,2)(0,2) +E (a.|.a.)*|(a|.a...) aa (0,2)(0,2) +E ab|a xabc (1,3) +E ab|a xxabc (2,4) +Ei (Ab|cD)* aBcD (0,4)(2,4) +BE [^-] --a (2,3) +BE [a-]* --a (0,3) +BE [a-m-]* --amoma-- (0,4) +E :::1:::0:|:::1:1:0: :::0:::1:::1:::0: (8,17) +E :::1:::0:|:::1:1:1: :::0:::1:::1:::0: (8,17) +{E [[:upper:]] A (0,1) [[]] not supported +E [[:lower:]]+ `az{ (1,3) +E [[:upper:]]+ @AZ[ (1,3) +BE [[-]] [[-]] (2,4) +BE [[.NIL.]] NULL ECOLLATE +BE [[=aleph=]] NULL ECOLLATE +} +BE$ \n \n (0,1) +BEn$ \n \n (0,1) +BE$ [^a] \n (0,1) +BE$ \na \na (0,2) +E (a)(b)(c) abc (0,3)(0,1)(1,2)(2,3) +BE xxx xxx (0,3) +E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) feb 6, (0,6) +E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) 2/7 (0,3) +E1 (^|[ (,;])((([Ff]eb[^ ]* *|0*2/|\* */?)0*[6-7]))([^0-9]|$) feb 1,Feb 6 (5,11) +E3 ((((((((((((((((((((((((((((((x)))))))))))))))))))))))))))))) x (0,1)(0,1)(0,1) +E3 ((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))* xx (0,2)(1,2)(1,2) +E a?(ab|ba)* ababababababababababababababababababababababababababababababababababababababababa (0,81)(79,81) +E abaa|abbaa|abbbaa|abbbbaa ababbabbbabbbabbbbabbbbaa (18,25) +E abaa|abbaa|abbbaa|abbbbaa ababbabbbabbbabbbbabaa (18,22) +E aaac|aabc|abac|abbc|baac|babc|bbac|bbbc baaabbbabac (7,11) +BE$ .* \x01\xff (0,2) +E aaaa|bbbb|cccc|ddddd|eeeeee|fffffff|gggg|hhhh|iiiii|jjjjj|kkkkk|llll XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa (53,57) +L aaaa\nbbbb\ncccc\nddddd\neeeeee\nfffffff\ngggg\nhhhh\niiiii\njjjjj\nkkkkk\nllll XaaaXbbbXcccXdddXeeeXfffXgggXhhhXiiiXjjjXkkkXlllXcbaXaaaa NOMATCH +E a*a*a*a*a*b aaaaaaaaab (0,10) +BE ^ NULL (0,0) +BE $ NULL (0,0) +BE ^$ NULL (0,0) +BE ^a$ a (0,1) +BE abc abc (0,3) +BE abc xabcy (1,4) +BE abc ababc (2,5) +BE ab*c abc (0,3) +BE ab*bc abc (0,3) +BE ab*bc abbc (0,4) +BE ab*bc abbbbc (0,6) +E ab+bc abbc (0,4) +E ab+bc abbbbc (0,6) +E ab?bc abbc (0,4) +E ab?bc abc (0,3) +E ab?c abc (0,3) +BE ^abc$ abc (0,3) +BE ^abc abcc (0,3) +BE abc$ aabc (1,4) +BE ^ abc (0,0) +BE $ abc (3,3) +BE a.c abc (0,3) +BE a.c axc (0,3) +BE a.*c axyzc (0,5) +BE a[bc]d abd (0,3) +BE a[b-d]e ace (0,3) +BE a[b-d] aac (1,3) +BE a[-b] a- (0,2) +BE a[b-] a- (0,2) +BE a] a] (0,2) +BE a[]]b a]b (0,3) +BE a[^bc]d aed (0,3) +BE a[^-b]c adc (0,3) +BE a[^]b]c adc (0,3) +E ab|cd abc (0,2) +E ab|cd abcd (0,2) +E a\(b a(b (0,3) +E a\(*b ab (0,2) +E a\(*b a((b (0,4) +E ((a)) abc (0,1)(0,1)(0,1) +E (a)b(c) abc (0,3)(0,1)(2,3) +E a+b+c aabbabc (4,7) +E a* aaa (0,3) +E (a*)* - (0,0)(0,0) +E (a*)+ - (0,0)(0,0) +E (a*|b)* - (0,0)(0,0) +E (a+|b)* ab (0,2)(1,2) +E (a+|b)+ ab (0,2)(1,2) +E (a+|b)? ab (0,1)(0,1) +BE [^ab]* cde (0,3) +E (^)* - (0,0)(0,0) +BE a* NULL (0,0) +E ([abc])*d abbbcd (0,6)(4,5) +E ([abc])*bcd abcd (0,4)(0,1) +E a|b|c|d|e e (0,1) +E (a|b|c|d|e)f ef (0,2)(0,1) +E ((a*|b))* - (0,0)(0,0)(0,0) +BE abcd*efg abcdefg (0,7) +BE ab* xabyabbbz (1,3) +BE ab* xayabbbz (1,2) +E (ab|cd)e abcde (2,5)(2,4) +BE [abhgefdc]ij hij (0,3) +E (a|b)c*d abcd (1,4)(1,2) +E (ab|ab*)bc abc (0,3)(0,1) +E a([bc]*)c* abc (0,3)(1,3) +E a([bc]*)(c*d) abcd (0,4)(1,3)(3,4) +E a([bc]+)(c*d) abcd (0,4)(1,3)(3,4) +E a([bc]*)(c+d) abcd (0,4)(1,2)(2,4) +E a[bcd]*dcdcde adcdcde (0,7) +E (ab|a)b*c abc (0,3)(0,2) +E ((a)(b)c)(d) abcd (0,4)(0,3)(0,1)(1,2)(3,4) +BE [A-Za-z_][A-Za-z0-9_]* alpha (0,5) +E ^a(bc+|b[eh])g|.h$ abh (1,3) +E (bc+d$|ef*g.|h?i(j|k)) effgz (0,5)(0,5) +E (bc+d$|ef*g.|h?i(j|k)) ij (0,2)(0,2)(1,2) +E (bc+d$|ef*g.|h?i(j|k)) reffgz (1,6)(1,6) +E (((((((((a))))))))) a (0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1)(0,1) +BE multiple words multiple words yeah (0,14) +E (.*)c(.*) abcde (0,5)(0,2)(3,5) +BE abcd abcd (0,4) +E a(bc)d abcd (0,4)(1,3) +E a[-]?c ac (0,3) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Qaddafi (0,15)(?,?)(10,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mo'ammar Gadhafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Kaddafi (0,15)(?,?)(10,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Qadhafi (0,15)(?,?)(10,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Gadafi (0,14)(?,?)(10,11) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mu'ammar Qadafi (0,15)(?,?)(11,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moamar Gaddafi (0,14)(?,?)(9,11) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Mu'ammar Qadhdhafi (0,18)(?,?)(13,15) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Khaddafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghaddafy (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghadafi (0,15)(?,?)(11,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Ghaddafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muamar Kaddafi (0,14)(?,?)(9,11) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Quathafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Muammar Gheddafi (0,16)(?,?)(11,13) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moammar Khadafy (0,15)(?,?)(11,12) +E M[ou]'?am+[ae]r .*([AEae]l[- ])?[GKQ]h?[aeu]+([dtz][dhz]?)+af[iy] Moammar Qudhafi (0,15)(?,?)(10,12) +E a+(b|c)*d+ aabcdd (0,6)(3,4) +E ^.+$ vivi (0,4) +E ^(.+)$ vivi (0,4)(0,4) +E ^([^!.]+).att.com!(.+)$ gryphon.att.com!eby (0,19)(0,7)(16,19) +E ^([^!]+!)?([^!]+)$ bas (0,3)(?,?)(0,3) +E ^([^!]+!)?([^!]+)$ bar!bas (0,7)(0,4)(4,7) +E ^([^!]+!)?([^!]+)$ foo!bas (0,7)(0,4)(4,7) +E ^.+!([^!]+!)([^!]+)$ foo!bar!bas (0,11)(4,8)(8,11) +E ((foo)|(bar))!bas bar!bas (0,7)(0,3)(?,?)(0,3) +E ((foo)|(bar))!bas foo!bar!bas (4,11)(4,7)(?,?)(4,7) +E ((foo)|(bar))!bas foo!bas (0,7)(0,3)(0,3) +E ((foo)|bar)!bas bar!bas (0,7)(0,3) +E ((foo)|bar)!bas foo!bar!bas (4,11)(4,7) +E ((foo)|bar)!bas foo!bas (0,7)(0,3)(0,3) +E (foo|(bar))!bas bar!bas (0,7)(0,3)(0,3) +E (foo|(bar))!bas foo!bar!bas (4,11)(4,7)(4,7) +E (foo|(bar))!bas foo!bas (0,7)(0,3) +E (foo|bar)!bas bar!bas (0,7)(0,3) +E (foo|bar)!bas foo!bar!bas (4,11)(4,7) +E (foo|bar)!bas foo!bas (0,7)(0,3) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas (0,11)(0,11)(?,?)(?,?)(4,8)(8,11) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bas (0,3)(?,?)(0,3) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ bar!bas (0,7)(0,4)(4,7) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bar!bas (0,11)(?,?)(?,?)(4,8)(8,11) +E ^([^!]+!)?([^!]+)$|^.+!([^!]+!)([^!]+)$ foo!bas (0,7)(0,4)(4,7) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bas (0,3)(0,3)(?,?)(0,3) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ bar!bas (0,7)(0,7)(0,4)(4,7) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bar!bas (0,11)(0,11)(?,?)(?,?)(4,8)(8,11) +E ^(([^!]+!)?([^!]+)|.+!([^!]+!)([^!]+))$ foo!bas (0,7)(0,7)(0,4)(4,7) +E .*(/XXX).* /XXX (0,4)(0,4) +E .*(\\XXX).* \XXX (0,4)(0,4) +E \\XXX \XXX (0,4) +E .*(/000).* /000 (0,4)(0,4) +E .*(\\000).* \000 (0,4)(0,4) +E \\000 \000 (0,4) diff --git a/hilti/src/3rdparty/justrx/test/retest.c b/hilti/src/3rdparty/justrx/test/retest.c new file mode 100644 index 000000000..ea15bbb87 --- /dev/null +++ b/hilti/src/3rdparty/justrx/test/retest.c @@ -0,0 +1,144 @@ +// $Id$ + +#include +#include +#include +#include + +#include + +static void print_error(int rc, regex_t* re, const char* prefix) +{ + char buffer[128]; + regerror(rc, re, buffer, sizeof(buffer)); + printf("%s, %s\n", prefix, buffer); +} + +static void do_match(char** argv, int argc, int opt, int options, char* data) +{ + const int max_captures = 20; + + int i; + int rc; + regex_t re; + regmatch_t pmatch[max_captures]; + + if ( (argc - opt) == 1 ) + rc = regcomp(&re, argv[opt], REG_EXTENDED | options); + else { + jrx_regset_init(&re, -1, REG_EXTENDED | options); + for ( i = opt; i < argc; i++ ) { + rc = jrx_regset_add(&re, argv[i], strlen(argv[i])); + if ( rc != 0 ) + break; + } + + rc = jrx_regset_finalize(&re); + } + + if ( rc != 0 ) { + print_error(rc, &re, "compile error"); + return; + } + + rc = regexec(&re, data, max_captures, pmatch, 0); + + if ( rc != 0 ) { + print_error(rc, &re, "pattern not found"); + return; + } + + printf("match found!\n"); + + for ( i = 0; i < max_captures; i++ ) { + if ( pmatch[i].rm_so != -1 ) + printf(" capture group #%d: (%d,%d)\n", i, pmatch[i].rm_so, pmatch[i].rm_eo); + } + + regfree(&re); +} + +char* readInput() +{ + const int chunk = 5; + char* buffer = 0; + int i = 0; + + while ( 1 ) { + buffer = realloc(buffer, (chunk * ++i) + 1); + if ( ! buffer ) { + fprintf(stderr, "cannot alloc\n"); + exit(1); + } + + char* p = buffer + (chunk * (i - 1)); + size_t n = fread(p, 1, chunk, stdin); + *(p + chunk) = '\0'; + + if ( feof(stdin) ) + break; + + if ( ferror(stdin) ) { + fprintf(stderr, "error while reading from stdin\n"); + exit(1); + } + } + + return buffer; +} + + +int main(int argc, char** argv) +{ + int opt = 1; + int debug = 0; + int lazy = 0; + + int i; + char* d; + + while ( argc > opt ) { + if ( strcmp(argv[opt], "-d") == 0 ) + debug = REG_DEBUG; + + else if ( strcmp(argv[opt], "-l") == 0 ) + lazy = REG_LAZY; + + else + break; + + ++opt; + } + + if ( (argc - opt) < 1 ) { + fprintf(stderr, "usage: echo 'data' | retest [-d] [-l] \n"); + return 1; + } + + char* data = readInput(); + + fprintf(stderr, "=== Pattern: %s\n", argv[opt]); + + for ( i = opt + 1; i < argc; i++ ) + fprintf(stderr, " %s\n", argv[i]); + + fputs("=== Data : ", stderr); + for ( d = data; *d; d++ ) { + if ( isprint(*d) ) + fputc(*d, stderr); + else + fprintf(stderr, "\\x%02x", (int)*d); + } + fputs("\n", stderr); + + fprintf(stderr, "\n=== Standard matcher with subgroups\n"); + do_match(argv, argc, opt, debug | lazy, data); + + fprintf(stderr, "\n=== Standard matcher without subgroups\n"); + do_match(argv, argc, opt, debug | lazy | REG_NOSUB | REG_STD_MATCHER, data); + + fprintf(stderr, "\n=== Minimal matcher\n"); + do_match(argv, argc, opt, debug | lazy | REG_NOSUB, data); + + exit(0); +} diff --git a/hilti/src/3rdparty/justrx/test/test_set.c b/hilti/src/3rdparty/justrx/test/test_set.c new file mode 100644 index 000000000..5775705d8 --- /dev/null +++ b/hilti/src/3rdparty/justrx/test/test_set.c @@ -0,0 +1,58 @@ + +DECLARE_SET(foo, int, int, SET_STD_EQUAL) + +static void print_set(set_foo* f) +{ + printf("size %d |", set_foo_size(f)); + set_for_each(foo, f, i) printf("%d ", i); + printf("\n"); +} + +int main(int argc, char** argv) +{ + set_foo* s = set_foo_create(0); + print_set(s); + printf("empty: %d (1)\n", set_foo_empty(s)); + set_foo_insert(s, 10); + set_foo_insert(s, 20); + set_foo_insert(s, 30); + set_foo_insert(s, 40); + set_foo_insert(s, 25); + set_foo_insert(s, 5); + set_foo_insert(s, 45); + print_set(s); + printf("empty: %d (0)\n", set_foo_empty(s)); + + set_foo_insert(s, 45); + set_foo_insert(s, 45); + print_set(s); + + set_foo_remove(s, 25); + set_foo_remove(s, 5); + set_foo_remove(s, 45); + print_set(s); + printf("empty: %d (0)\n", set_foo_empty(s)); + + set_foo* o = set_foo_create(0); + set_foo_insert(o, 10); + set_foo_insert(o, 20); + set_foo_insert(o, 30); + set_foo_insert(o, 40); + + printf("equal: %d (1)\n", set_foo_equal(s, s)); + printf("equal: %d (1)\n", set_foo_equal(s, o)); + set_foo_remove(o, 10); + printf("equal: %d (0)\n", set_foo_equal(s, o)); + set_foo_insert(o, 1100); + printf("equal: %d (0)\n", set_foo_equal(s, o)); + + set_foo_remove(s, 10); + set_foo_remove(s, 20); + set_foo_remove(s, 30); + set_foo_remove(s, 40); + set_foo_remove(s, 25); + set_foo_remove(s, 5); + set_foo_remove(s, 45); + print_set(s); + printf("empty: %d (1)\n", set_foo_empty(s)); +} diff --git a/hilti/src/3rdparty/justrx/test/testregex.c b/hilti/src/3rdparty/justrx/test/testregex.c new file mode 100644 index 000000000..38cc144eb --- /dev/null +++ b/hilti/src/3rdparty/justrx/test/testregex.c @@ -0,0 +1,2102 @@ +/* + * regex(3) test harness + * + * build: cc -o testregex testregex.c + * help: testregex --man + * note: REG_* features are detected by #ifdef; if REG_* are enums + * then supply #define REG_foo REG_foo for each enum REG_foo + * + * Glenn Fowler + * AT&T Research + * + * PLEASE: publish your tests so everyone can benefit + * + * The following license covers testregex.c and all associated test data. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following disclaimer: + * + * THIS SOFTWARE IS PROVIDED BY AT&T ``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 AT&T 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. + */ + +static const char id[] = "\n@(#)$Id: testregex (AT&T Research) 2008-05-15 $\0\n"; + +#if _PACKAGE_ast +#include +#else +#include +#endif + +#include "./regex.h" +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#include +#endif + +#if ! _PACKAGE_ast +#undef REG_DISCIPLINE +#endif + +#ifndef REG_DELIMITED +#undef _REG_subcomp +#endif + +#define TEST_ARE 0x00000001 +#define TEST_BRE 0x00000002 +#define TEST_ERE 0x00000004 +#define TEST_KRE 0x00000008 +#define TEST_LRE 0x00000010 +#define TEST_SRE 0x00000020 + +#define TEST_EXPAND 0x00000040 +#define TEST_LENIENT 0x00000080 + +#define TEST_QUERY 0x00000100 +#define TEST_SUB 0x00000200 +#define TEST_UNSPECIFIED 0x00000400 +#define TEST_VERIFY 0x00000800 +#define TEST_AND 0x00001000 +#define TEST_OR 0x00002000 + +#define TEST_DELIMIT 0x00010000 +#define TEST_OK 0x00020000 +#define TEST_SAME 0x00040000 + +#define TEST_ACTUAL 0x00100000 +#define TEST_BASELINE 0x00200000 +#define TEST_FAIL 0x00400000 +#define TEST_PASS 0x00800000 +#define TEST_SUMMARY 0x01000000 + +#define TEST_IGNORE_ERROR 0x02000000 +#define TEST_IGNORE_OVER 0x04000000 +#define TEST_IGNORE_POSITION 0x08000000 + +#define TEST_CATCH 0x10000000 +#define TEST_VERBOSE 0x20000000 + +#define TEST_DECOMP 0x40000000 + +#define TEST_GLOBAL \ + (TEST_ACTUAL | TEST_AND | TEST_BASELINE | TEST_CATCH | TEST_FAIL | TEST_IGNORE_ERROR | \ + TEST_IGNORE_OVER | TEST_IGNORE_POSITION | TEST_OR | TEST_PASS | TEST_SUMMARY | TEST_VERBOSE) + +#ifdef REG_DISCIPLINE + + +#include + +typedef struct Disc_s { + regdisc_t disc; + int ordinal; + Sfio_t* sp; +} Disc_t; + +static void* compf(const regex_t* re, const char* xstr, size_t xlen, regdisc_t* disc) +{ + Disc_t* dp = (Disc_t*)disc; + + return (void*)++dp->ordinal; +} + +static int execf(const regex_t* re, void* data, const char* xstr, size_t xlen, const char* sstr, + size_t slen, char** snxt, regdisc_t* disc) +{ + Disc_t* dp = (Disc_t*)disc; + + sfprintf(dp->sp, "{%-.*s}(%d:%d)", xlen, xstr, (int)data, slen); + return atoi(xstr); +} + +static void* resizef(void* handle, void* data, size_t size) +{ + if ( ! size ) + return 0; + return stkalloc((Sfio_t*)handle, size); +} + +#endif + +#ifndef NiL +#ifdef __STDC__ +#define NiL 0 +#else +#define NiL (char*)0 +#endif +#endif + +#define H(x) \ + do { \ + if ( html ) \ + fprintf(stderr, x); \ + } while ( 0 ) +#define T(x) fprintf(stderr, x) + +static void help(int html) +{ + H("\n"); + H("\n"); + H("\n"); + H("testregex man document\n"); + H("\n"); + H("\n"); + H("
\n");
+    T("NAME\n");
+    T("  testregex - regex(3) test harness\n");
+    T("\n");
+    T("SYNOPSIS\n");
+    T("  testregex [ options ]\n");
+    T("\n");
+    T("DESCRIPTION\n");
+    T("  testregex reads regex(3) test specifications, one per line, from the\n");
+    T("  standard input and writes one output line for each failed test. A\n");
+    T("  summary line is written after all tests are done. Each successful\n");
+    T("  test is run again with REG_NOSUB. Unsupported features are noted\n");
+    T("  before the first test, and tests requiring these features are\n");
+    T("  silently ignored.\n");
+    T("\n");
+    T("OPTIONS\n");
+    T("  -c	catch signals and non-terminating calls\n");
+    T("  -e	ignore error return mismatches\n");
+    T("  -h	list help on standard error\n");
+    T("  -n	do not repeat successful tests with regnexec()\n");
+    T("  -o	ignore match[] overrun errors\n");
+    T("  -p	ignore negative position mismatches\n");
+    T("  -s	use stack instead of malloc\n");
+    T("  -x	do not repeat successful tests with REG_NOSUB\n");
+    T("  -v	list each test line\n");
+    T("  -A	list failed test lines with actual answers\n");
+    T("  -B	list all test lines with actual answers\n");
+    T("  -F	list failed test lines\n");
+    T("  -P	list passed test lines\n");
+    T("  -S	output one summary line\n");
+    T("\n");
+    T("INPUT FORMAT\n");
+    T("  Input lines may be blank, a comment beginning with #, or a test\n");
+    T("  specification. A specification is five fields separated by one\n");
+    T("  or more tabs. NULL denotes the empty string and NIL denotes the\n");
+    T("  0 pointer.\n");
+    T("\n");
+    T("  Field 1: the regex(3) flags to apply, one character per REG_feature\n");
+    T("  flag. The test is skipped if REG_feature is not supported by the\n");
+    T("  implementation. If the first character is not [BEASKL] then the\n");
+    T("  specification is a global control line. One or more of [BEASKL] may be\n");
+    T("  specified; the test will be repeated for each mode.\n");
+    T("\n");
+    T("    B 	basic			BRE	(grep, ed, sed)\n");
+    T("    E 	REG_EXTENDED		ERE	(egrep)\n");
+    T("    A	REG_AUGMENTED		ARE	(egrep with negation)\n");
+    T("    S	REG_SHELL		SRE	(sh glob)\n");
+    T("    K	REG_SHELL|REG_AUGMENTED	KRE	(ksh glob)\n");
+    T("    L	REG_LITERAL		LRE	(fgrep)\n");
+    T("\n");
+    T("    a	REG_LEFT|REG_RIGHT	implicit ^...$\n");
+    T("    b	REG_NOTBOL		lhs does not match ^\n");
+    T("    c	REG_COMMENT		ignore space and #...\\n\n");
+    T("    d	REG_SHELL_DOT		explicit leading . match\n");
+    T("    e	REG_NOTEOL		rhs does not match $\n");
+    T("    f	REG_MULTIPLE		multiple \\n separated patterns\n");
+    T("    g	FNM_LEADING_DIR		testfnmatch only -- match until /\n");
+    T("    h	REG_MULTIREF		multiple digit backref\n");
+    T("    i	REG_ICASE		ignore case\n");
+    T("    j	REG_SPAN		. matches \\n\n");
+    T("    k	REG_ESCAPE		\\ to ecape [...] delimiter\n");
+    T("    l	REG_LEFT		implicit ^...\n");
+    T("    m	REG_MINIMAL		minimal match\n");
+    T("    n	REG_NEWLINE		explicit \\n match\n");
+    T("    o	REG_ENCLOSED		(|&) magic inside [@|&](...)\n");
+    T("    p	REG_SHELL_PATH		explicit / match\n");
+    T("    q	REG_DELIMITED		delimited pattern\n");
+    T("    r	REG_RIGHT		implicit ...$\n");
+    T("    s	REG_SHELL_ESCAPED	\\ not special\n");
+    T("    t	REG_MUSTDELIM		all delimiters must be specified\n");
+    T("    u	standard unspecified behavior -- errors not counted\n");
+    T("    w	REG_NOSUB		no subexpression match array\n");
+    T("    x	REG_LENIENT		let some errors slide\n");
+    T("    y	REG_LEFT		regexec() implicit ^...\n");
+    T("    z	REG_NULL		NULL subexpressions ok\n");
+    T("    $	                        expand C \\c escapes in fields 2 and 3\n");
+    T("    /	                        field 2 is a regsubcomp() expression\n");
+    T("    =	                        field 3 is a regdecomp() expression\n");
+    T("\n");
+    T("  Field 1 control lines:\n");
+    T("\n");
+    T("    C		set LC_COLLATE and LC_CTYPE to locale in field 2\n");
+    T("\n");
+    T("    ?test ...	output field 5 if passed and != EXPECTED, silent otherwise\n");
+    T("    &test ...	output field 5 if current and previous passed\n");
+    T("    |test ...	output field 5 if current passed and previous failed\n");
+    T("    ; ...	output field 2 if previous failed\n");
+    T("    {test ...	skip if failed until }\n");
+    T("    }		end of skip\n");
+    T("\n");
+    T("    : comment		comment copied as output NOTE\n");
+    T("    :comment:test	:comment: ignored\n");
+    T("    N[OTE] comment	comment copied as output NOTE\n");
+    T("    T[EST] comment	comment\n");
+    T("\n");
+    T("    number		use number for nmatch (20 by default)\n");
+    T("\n");
+    T("  Field 2: the regular expression pattern; SAME uses the pattern from\n");
+    T("    the previous specification.\n");
+    T("\n");
+    T("  Field 3: the string to match.\n");
+    T("\n");
+    T("  Field 4: the test outcome. This is either one of the posix error\n");
+    T("    codes (with REG_ omitted) or the match array, a list of (m,n)\n");
+    T("    entries with m and n being first and last+1 positions in the\n");
+    T("    field 3 string, or NULL if REG_NOSUB is in effect and success\n");
+    T("    is expected. BADPAT is acceptable in place of any regcomp(3)\n");
+    T("    error code. The match[] array is initialized to (-2,-2) before\n");
+    T("    each test. All array elements from 0 to nmatch-1 must be specified\n");
+    T("    in the outcome. Unspecified endpoints (offset -1) are denoted by ?.\n");
+    T("    Unset endpoints (offset -2) are denoted by X. {x}(o:n) denotes a\n");
+    T("    matched (?{...}) expression, where x is the text enclosed by {...},\n");
+    T("    o is the expression ordinal counting from 1, and n is the length of\n");
+    T("    the unmatched portion of the subject string. If x starts with a\n");
+    T("    number then that is the return value of re_execf(), otherwise 0 is\n");
+    T("    returned.\n");
+    T("\n");
+    T("  Field 5: optional comment appended to the report.\n");
+    T("\n");
+    T("CAVEAT\n");
+    T("    If a regex implementation misbehaves with memory then all bets are off.\n");
+    T("\n");
+    T("CONTRIBUTORS\n");
+    T("  Glenn Fowler    gsf@research.att.com        (ksh strmatch, regex extensions)\n");
+    T("  David Korn      dgk@research.att.com        (ksh glob matcher)\n");
+    T("  Doug McIlroy    mcilroy@dartmouth.edu       (ast regex/testre in C++)\n");
+    T("  Tom Lord        lord@regexps.com            (rx tests)\n");
+    T("  Henry Spencer   henry@zoo.toronto.edu       (original public regex)\n");
+    T("  Andrew Hume     andrew@research.att.com     (gre tests)\n");
+    T("  John Maddock    John_Maddock@compuserve.com (regex++ tests)\n");
+    T("  Philip Hazel    ph10@cam.ac.uk              (pcre tests)\n");
+    T("  Ville Laurikari vl@iki.fi                   (libtre tests)\n");
+    H("
\n"); + H("\n"); + H("\n"); +} + +#ifndef elementsof +#define elementsof(x) (sizeof(x) / sizeof(x[0])) +#endif + +#ifndef streq +#define streq(a, b) (*(a) == *(b) && ! strcmp(a, b)) +#endif + +#define HUNG 2 +#define NOTEST (~0) + +#ifndef REG_TEST_DEFAULT +#define REG_TEST_DEFAULT 0 +#endif + +#ifndef REG_EXEC_DEFAULT +#define REG_EXEC_DEFAULT 0 +#endif + +static const char* unsupported[] = {"BASIC", +#ifndef REG_EXTENDED + "EXTENDED", +#endif +#ifndef REG_AUGMENTED + "AUGMENTED", +#endif +#ifndef REG_SHELL + "SHELL", +#endif + +#ifndef REG_COMMENT + "COMMENT", +#endif +#ifndef REG_DELIMITED + "DELIMITED", +#endif +#ifndef REG_DISCIPLINE + "DISCIPLINE", +#endif +#ifndef REG_ESCAPE + "ESCAPE", +#endif +#ifndef REG_ICASE + "ICASE", +#endif +#ifndef REG_LEFT + "LEFT", +#endif +#ifndef REG_LENIENT + "LENIENT", +#endif +#ifndef REG_LITERAL + "LITERAL", +#endif +#ifndef REG_MINIMAL + "MINIMAL", +#endif +#ifndef REG_MULTIPLE + "MULTIPLE", +#endif +#ifndef REG_MULTIREF + "MULTIREF", +#endif +#ifndef REG_MUSTDELIM + "MUSTDELIM", +#endif +#ifndef REG_NEWLINE + "NEWLINE", +#endif +#ifndef REG_NOTBOL + "NOTBOL", +#endif +#ifndef REG_NOTEOL + "NOTEOL", +#endif +#ifndef REG_NULL + "NULL", +#endif +#ifndef REG_RIGHT + "RIGHT", +#endif +#ifndef REG_SHELL_DOT + "SHELL_DOT", +#endif +#ifndef REG_SHELL_ESCAPED + "SHELL_ESCAPED", +#endif +#ifndef REG_SHELL_GROUP + "SHELL_GROUP", +#endif +#ifndef REG_SHELL_PATH + "SHELL_PATH", +#endif +#ifndef REG_SPAN + "SPAN", +#endif +#if REG_NOSUB & REG_TEST_DEFAULT + "SUBMATCH", +#endif +#if ! _REG_nexec + "regnexec", +#endif +#if ! _REG_subcomp + "regsubcomp", +#endif +#if ! _REG_decomp + "redecomp", +#endif + 0}; + +#ifndef REG_COMMENT +#define REG_COMMENT NOTEST +#endif +#ifndef REG_DELIMITED +#define REG_DELIMITED NOTEST +#endif +#ifndef REG_ESCAPE +#define REG_ESCAPE NOTEST +#endif +#ifndef REG_ICASE +#define REG_ICASE NOTEST +#endif +#ifndef REG_LEFT +#define REG_LEFT NOTEST +#endif +#ifndef REG_LENIENT +#define REG_LENIENT 0 +#endif +#ifndef REG_MINIMAL +#define REG_MINIMAL NOTEST +#endif +#ifndef REG_MULTIPLE +#define REG_MULTIPLE NOTEST +#endif +#ifndef REG_MULTIREF +#define REG_MULTIREF NOTEST +#endif +#ifndef REG_MUSTDELIM +#define REG_MUSTDELIM NOTEST +#endif +#ifndef REG_NEWLINE +#define REG_NEWLINE NOTEST +#endif +#ifndef REG_NOTBOL +#define REG_NOTBOL NOTEST +#endif +#ifndef REG_NOTEOL +#define REG_NOTEOL NOTEST +#endif +#ifndef REG_NULL +#define REG_NULL NOTEST +#endif +#ifndef REG_RIGHT +#define REG_RIGHT NOTEST +#endif +#ifndef REG_SHELL_DOT +#define REG_SHELL_DOT NOTEST +#endif +#ifndef REG_SHELL_ESCAPED +#define REG_SHELL_ESCAPED NOTEST +#endif +#ifndef REG_SHELL_GROUP +#define REG_SHELL_GROUP NOTEST +#endif +#ifndef REG_SHELL_PATH +#define REG_SHELL_PATH NOTEST +#endif +#ifndef REG_SPAN +#define REG_SPAN NOTEST +#endif + +#define REG_UNKNOWN (-1) + +#ifndef REG_ENEWLINE +#define REG_ENEWLINE (REG_UNKNOWN - 1) +#endif +#ifndef REG_ENULL +#ifndef REG_EMPTY +#define REG_ENULL (REG_UNKNOWN - 2) +#else +#define REG_ENULL REG_EMPTY +#endif +#endif +#ifndef REG_ECOUNT +#define REG_ECOUNT (REG_UNKNOWN - 3) +#endif +#ifndef REG_BADESC +#define REG_BADESC (REG_UNKNOWN - 4) +#endif +#ifndef REG_EMEM +#define REG_EMEM (REG_UNKNOWN - 5) +#endif +#ifndef REG_EHUNG +#define REG_EHUNG (REG_UNKNOWN - 6) +#endif +#ifndef REG_EBUS +#define REG_EBUS (REG_UNKNOWN - 7) +#endif +#ifndef REG_EFAULT +#define REG_EFAULT (REG_UNKNOWN - 8) +#endif +#ifndef REG_EFLAGS +#define REG_EFLAGS (REG_UNKNOWN - 9) +#endif +#ifndef REG_EDELIM +#define REG_EDELIM (REG_UNKNOWN - 9) +#endif + +static const struct { + int code; + char* name; +} codes[] = { + REG_UNKNOWN, "UNKNOWN", REG_NOMATCH, "NOMATCH", REG_BADPAT, "BADPAT", REG_ECOLLATE, + "ECOLLATE", REG_ECTYPE, "ECTYPE", REG_EESCAPE, "EESCAPE", REG_ESUBREG, "ESUBREG", + REG_EBRACK, "EBRACK", REG_EPAREN, "EPAREN", REG_EBRACE, "EBRACE", REG_BADBR, + "BADBR", REG_ERANGE, "ERANGE", REG_ESPACE, "ESPACE", REG_BADRPT, "BADRPT", + REG_ENEWLINE, "ENEWLINE", REG_ENULL, "ENULL", REG_ECOUNT, "ECOUNT", REG_BADESC, + "BADESC", REG_EMEM, "EMEM", REG_EHUNG, "EHUNG", REG_EBUS, "EBUS", + REG_EFAULT, "EFAULT", REG_EFLAGS, "EFLAGS", REG_EDELIM, "EDELIM", +}; + +static struct { + regmatch_t NOMATCH; + int errors; + int extracted; + int ignored; + int lineno; + int passed; + int signals; + int unspecified; + int verify; + int warnings; + char* file; + char* stack; + char* which; + jmp_buf gotcha; +#ifdef REG_DISCIPLINE + Disc_t disc; +#endif +} state; + +static void quote(char* s, int len, unsigned long test) +{ + unsigned char* u = (unsigned char*)s; + unsigned char* e; + int c; +#ifdef MB_CUR_MAX + int w; +#endif + + if ( ! u ) + printf("NIL"); + else if ( ! *u && len <= 1 ) + printf("NULL"); + else if ( test & TEST_EXPAND ) { + if ( len < 0 ) + len = strlen((char*)u); + e = u + len; + if ( test & TEST_DELIMIT ) + printf("\""); + while ( u < e ) + switch ( c = *u++ ) { + case '\\': + printf("\\\\"); + break; + case '"': + if ( test & TEST_DELIMIT ) + printf("\\\""); + else + printf("\""); + break; + case '\a': + printf("\\a"); + break; + case '\b': + printf("\\b"); + break; + case 033: + printf("\\e"); + break; + case '\f': + printf("\\f"); + break; + case '\n': + printf("\\n"); + break; + case '\r': + printf("\\r"); + break; + case '\t': + printf("\\t"); + break; + case '\v': + printf("\\v"); + break; + default: +#ifdef MB_CUR_MAX + s = (char*)u - 1; + if ( (w = mblen(s, (char*)e - s)) > 1 ) { + u += w - 1; + fwrite(s, 1, w, stdout); + } + else +#endif + if ( ! iscntrl(c) && isprint(c) ) + putchar(c); + else + printf("\\x%02x", c); + break; + } + if ( test & TEST_DELIMIT ) + printf("\""); + } + else + printf("%s", s); +} + +static void report(char* comment, char* fun, char* re, char* s, int len, char* msg, int flags, + unsigned long test) +{ + if ( state.file ) + printf("%s:", state.file); + printf("%d:", state.lineno); + if ( re ) { + printf(" "); + quote(re, -1, test | TEST_DELIMIT); + if ( s ) { + printf(" versus "); + quote(s, len, test | TEST_DELIMIT); + } + } + if ( test & TEST_UNSPECIFIED ) { + state.unspecified++; + printf(" unspecified behavior"); + } + else + state.errors++; + if ( state.which ) + printf(" %s", state.which); + if ( flags & REG_NOSUB ) + printf(" NOSUB"); + if ( fun ) + printf(" %s", fun); + if ( comment[strlen(comment) - 1] == '\n' ) + printf(" %s", comment); + else { + printf(" %s: ", comment); + if ( msg ) + printf("%s: ", msg); + } +} + +static void error(regex_t* preg, int code) +{ + char* msg; + char buf[256]; + + switch ( code ) { + case REG_EBUS: + msg = "bus error"; + break; + case REG_EFAULT: + msg = "memory fault"; + break; + case REG_EHUNG: + msg = "did not terminate"; + break; + default: + regerror(code, preg, msg = buf, sizeof buf); + break; + } + printf("%s\n", msg); +} + +static void bad(char* comment, char* re, char* s, int len, unsigned long test) +{ + printf("bad test case "); + report(comment, NiL, re, s, len, NiL, 0, test); + exit(1); +} + +static int escape(char* s) +{ + char* b; + char* t; + char* q; + char* e; + int c; + + for ( b = t = s; *t == *s; s++, t++ ) + if ( *s == '\\' ) + switch ( *++s ) { + case '\\': + break; + case 'a': + *t = '\a'; + break; + case 'b': + *t = '\b'; + break; + case 'c': + if ( *t == *++s ) + *t &= 037; + else + s--; + break; + case 'e': + case 'E': + *t = 033; + break; + case 'f': + *t = '\f'; + break; + case 'n': + *t = '\n'; + break; + case 'r': + *t = '\r'; + break; + case 's': + *t = ' '; + break; + case 't': + *t = '\t'; + break; + case 'v': + *t = '\v'; + break; + case 'u': + case 'x': + c = 0; + q = c == 'u' ? (s + 5) : (char*)0; + e = s + 1; + while ( ! e || ! q || s < q ) { + switch ( *++s ) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + c = (c << 4) + *s - 'a' + 10; + continue; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + c = (c << 4) + *s - 'A' + 10; + continue; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + c = (c << 4) + *s - '0'; + continue; + case '{': + case '[': + if ( s != e ) { + s--; + break; + } + e = 0; + continue; + case '}': + case ']': + if ( e ) + s--; + break; + default: + s--; + break; + } + break; + } + *t = c; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = *s - '0'; + q = s + 2; + while ( s < q ) { + switch ( *++s ) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = (c << 3) + *s - '0'; + break; + default: + q = --s; + break; + } + } + *t = c; + break; + default: + *(s + 1) = 0; + bad("invalid C \\ escape\n", s - 1, NiL, 0, 0); + } + return t - b; +} + +static void matchoffprint(int off) +{ + switch ( off ) { + case -2: + printf("X"); + break; + case -1: + printf("?"); + break; + default: + printf("%d", off); + break; + } +} + +static void matchprint(regmatch_t* match, int nmatch, int nsub, char* ans, unsigned long test) +{ + int i; + + for ( ; nmatch > nsub + 1; nmatch-- ) + if ( (match[nmatch - 1].rm_so != -1 || match[nmatch - 1].rm_eo != -1) && + (! (test & TEST_IGNORE_POSITION) || + (match[nmatch - 1].rm_so >= 0 && match[nmatch - 1].rm_eo >= 0)) ) + break; + for ( i = 0; i < nmatch; i++ ) { + printf("("); + matchoffprint(match[i].rm_so); + printf(","); + matchoffprint(match[i].rm_eo); + printf(")"); + } + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE)) ) { + if ( ans ) + printf(" expected: %s", ans); + printf("\n"); + } +} + +static int matchcheck(regmatch_t* match, int nmatch, int nsub, char* ans, char* re, char* s, + int len, int flags, unsigned long test) +{ + char* p; + int i; + int m; + int n; + + if ( streq(ans, "OK") ) + return test & (TEST_BASELINE | TEST_PASS | TEST_VERIFY); + for ( i = 0, p = ans; i < nmatch && *p; i++ ) { + if ( *p == '{' ) { +#ifdef REG_DISCIPLINE + char* x; + + if ( ! (x = sfstruse(state.disc.sp)) ) + bad("out of space [discipline string]\n", NiL, NiL, 0, 0); + if ( strcmp(p, x) ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + return 0; + report("callout failed", NiL, re, s, len, NiL, flags, test); + quote(p, -1, test); + printf(" expected, "); + quote(x, -1, test); + printf(" returned\n"); + } +#endif + break; + } + if ( *p++ != '(' ) + bad("improper answer\n", re, s, -1, test); + if ( *p == '?' ) { + m = -1; + p++; + } + else + m = strtol(p, &p, 10); + if ( *p++ != ',' ) + bad("improper answer\n", re, s, -1, test); + if ( *p == '?' ) { + n = -1; + p++; + } + else + n = strtol(p, &p, 10); + if ( *p++ != ')' ) + bad("improper answer\n", re, s, -1, test); + if ( m != match[i].rm_so || n != match[i].rm_eo ) { + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY)) ) { + report("failed: match was", NiL, re, s, len, NiL, flags, test); + matchprint(match, nmatch, nsub, ans, test); + } + return 0; + } + } + for ( ; i < nmatch; i++ ) { + if ( match[i].rm_so != -1 || match[i].rm_eo != -1 ) { + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_VERIFY)) ) { + if ( (test & TEST_IGNORE_POSITION) && (match[i].rm_so < 0 || match[i].rm_eo < 0) ) { + state.ignored++; + return 0; + } + if ( ! (test & TEST_SUMMARY) ) { + report("failed: match was", NiL, re, s, len, NiL, flags, test); + matchprint(match, nmatch, nsub, ans, test); + } + } + return 0; + } + } + if ( ! (test & TEST_IGNORE_OVER) && match[nmatch].rm_so != state.NOMATCH.rm_so ) { + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY)) ) { + report("failed: overran match array", NiL, re, s, len, NiL, flags, test); + matchprint(match, nmatch + 1, nsub, NiL, test); + } + return 0; + } + return 1; +} + +static void sigunblock(int s) +{ +#ifdef SIG_SETMASK + int op; + sigset_t mask; + + sigemptyset(&mask); + if ( s ) { + sigaddset(&mask, s); + op = SIG_UNBLOCK; + } + else + op = SIG_SETMASK; + sigprocmask(op, &mask, NiL); +#else +#ifdef sigmask + sigsetmask(s ? (sigsetmask(0L) & ~sigmask(s)) : 0L); +#endif +#endif +} + +static void gotcha(int sig) +{ + int ret; + + signal(sig, gotcha); + alarm(0); + state.signals++; + switch ( sig ) { + case SIGALRM: + ret = REG_EHUNG; + break; + case SIGBUS: + ret = REG_EBUS; + break; + default: + ret = REG_EFAULT; + break; + } + sigunblock(sig); + longjmp(state.gotcha, ret); +} + +static char* mygetline(FILE* fp) +{ + static char buf[32 * 1024]; + + register char* s = buf; + register char* e = &buf[sizeof(buf)]; + register char* b; + + for ( ;; ) { + if ( ! (b = fgets(s, e - s, fp)) ) + return 0; + state.lineno++; + s += strlen(s); + if ( s == b || *--s != '\n' || s == b || *(s - 1) != '\\' ) { + *s = 0; + break; + } + s--; + } + return buf; +} + +static unsigned long note(unsigned long level, char* msg, unsigned long skip, unsigned long test) +{ + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_SUMMARY)) && + ! skip ) { + printf("NOTE\t"); + if ( msg ) + printf("%s: ", msg); + printf("skipping lines %d", state.lineno); + } + return skip | level; +} + +#define TABS(n) &ts[7 - ((n)&7)] + +static char ts[] = "\t\t\t\t\t\t\t"; + +static unsigned long extract(int* tabs, char* spec, char* re, char* s, char* ans, char* msg, + char* accept, regmatch_t* match, int nmatch, int nsub, + unsigned long skip, unsigned long level, unsigned long test) +{ + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_OK | TEST_PASS | TEST_SUMMARY) ) { + state.extracted = 1; + if ( test & TEST_OK ) { + state.passed++; + if ( (test & TEST_VERIFY) && + ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_SUMMARY)) ) { + if ( msg && strcmp(msg, "EXPECTED") != 0 ) + printf("NOTE\t%s\n", msg); + return skip; + } + test &= ~(TEST_PASS | TEST_QUERY); + } + if ( test & (TEST_QUERY | TEST_VERIFY) ) { + if ( test & TEST_BASELINE ) + test &= ~(TEST_BASELINE | TEST_PASS); + else + test |= TEST_PASS; + skip |= level; + } + if ( ! (test & TEST_OK) ) { + if ( test & TEST_UNSPECIFIED ) + state.unspecified++; + else + state.errors++; + } + if ( test & (TEST_PASS | TEST_SUMMARY) ) + return skip; + test &= ~TEST_DELIMIT; + printf("%s%s", spec, TABS(*tabs++)); + if ( (test & (TEST_BASELINE | TEST_SAME)) == (TEST_BASELINE | TEST_SAME) ) + printf("SAME"); + else + quote(re, -1, test); + printf("%s", TABS(*tabs++)); + quote(s, -1, test); + printf("%s", TABS(*tabs++)); + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE)) || (! accept && ! match) ) + printf("%s", ans); + else if ( accept ) + printf("%s", accept); + else + matchprint(match, nmatch, nsub, NiL, test); + if ( msg ) + printf("%s%s", TABS(*tabs++), msg); + putchar('\n'); + } + else if ( test & TEST_QUERY ) + skip = note(level, msg, skip, test); + else if ( test & TEST_VERIFY ) + state.extracted = 1; + return skip; +} + +static int catchfree(regex_t* preg, int flags, int* tabs, char* spec, char* re, char* s, char* ans, + char* msg, char* accept, regmatch_t* match, int nmatch, int nsub, + unsigned long skip, unsigned long level, unsigned long test) +{ + int eret; + + if ( ! (test & TEST_CATCH) ) { + regfree(preg); + eret = 0; + } + else if ( ! (eret = setjmp(state.gotcha)) ) { + alarm(HUNG); + regfree(preg); + alarm(0); + } + else if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + extract(tabs, spec, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); + else { + report("failed", "regfree", re, NiL, -1, msg, flags, test); + error(preg, eret); + } + return eret; +} + +int main(int argc, char** argv) +{ + int flags; + int cflags; + int eflags; + int nmatch; + int nexec; + int nstr; + int cret; + int eret; + int nsub; + int i; + int j; + int expected; + int got; + int locale; + int subunitlen; + int testno; + unsigned long level; + unsigned long skip; + char* p; + char* line; + char* spec; + char* re; + char* s; + char* ans; + char* msg; + char* fun; + char* ppat; + char* subunit; + char* version; + char* field[6]; + char* delim[6]; + FILE* fp; + int tabs[6]; + char unit[64]; + regmatch_t match[100]; + regex_t preg; + + static char pat[32 * 1024]; + + int nonosub = REG_NOSUB == 0; + int nonexec = 0; + + unsigned long test = 0; + + static char* filter[] = {"-", 0}; + + state.NOMATCH.rm_so = state.NOMATCH.rm_eo = -2; + p = unit; + version = (char*)id + 10; + while ( p < &unit[sizeof(unit) - 1] && (*p = *version++) && ! isspace(*p) ) + p++; + *p = 0; + while ( (p = *++argv) && *p == '-' ) + for ( ;; ) { + switch ( *++p ) { + case 0: + break; + case 'c': + test |= TEST_CATCH; + continue; + case 'e': + test |= TEST_IGNORE_ERROR; + continue; + case 'l': + flags |= REG_LAZY; + continue; + case 'h': + case '?': + help(0); + return 2; + case '-': + help(p[1] == 'h'); + return 2; + case 'n': + nonexec = 1; + continue; + case 'o': + test |= TEST_IGNORE_OVER; + continue; + case 'p': + test |= TEST_IGNORE_POSITION; + continue; + case 's': +#ifdef REG_DISCIPLINE + if ( ! (state.stack = stkalloc(stkstd, 0)) ) + fprintf(stderr, "%s: out of space [stack]", unit); + state.disc.disc.re_resizef = resizef; + state.disc.disc.re_resizehandle = (void*)stkstd; +#endif + continue; + case 'x': + nonosub = 1; + continue; + case 'v': + test |= TEST_VERBOSE; + continue; + case 'A': + test |= TEST_ACTUAL; + continue; + case 'B': + test |= TEST_BASELINE; + continue; + case 'F': + test |= TEST_FAIL; + continue; + case 'P': + test |= TEST_PASS; + continue; + case 'S': + test |= TEST_SUMMARY; + continue; + default: + fprintf(stderr, "%s: %c: invalid option\n", unit, *p); + return 2; + } + break; + } + if ( ! *argv ) + argv = filter; + locale = 0; + while ( state.file == *argv++ ) { + if ( streq(state.file, "-") || streq(state.file, "/dev/stdin") || + streq(state.file, "/dev/fd/0") ) { + state.file = 0; + fp = stdin; + } + else if ( ! (fp = fopen(state.file, "re")) ) { + fprintf(stderr, "%s: %s: cannot read\n", unit, state.file); + return 2; + } + testno = state.errors = state.ignored = state.lineno = state.passed = state.signals = + state.unspecified = state.warnings = 0; + skip = 0; + level = 1; + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_SUMMARY)) ) { + printf("TEST\t%s ", unit); + if ( s == state.file ) { + subunit = p = 0; + for ( ;; ) { + switch ( *s++ ) { + case 0: + break; + case '/': + subunit = s; + continue; + case '.': + p = s - 1; + continue; + default: + continue; + } + break; + } + if ( ! subunit ) + subunit = state.file; + if ( p < subunit ) + p = s - 1; + subunitlen = p - subunit; + printf("%-.*s ", subunitlen, subunit); + } + else + subunit = 0; + for ( s = version; *s && (*s != ' ' || *(s + 1) != '$'); s++ ) + putchar(*s); + if ( test & TEST_CATCH ) + printf(", catch"); + if ( test & TEST_IGNORE_ERROR ) + printf(", ignore error code mismatches"); + if ( test & TEST_IGNORE_POSITION ) + printf(", ignore negative position mismatches"); +#ifdef REG_DISCIPLINE + if ( state.stack ) + printf(", stack"); +#endif + if ( test & TEST_VERBOSE ) + printf(", verbose"); + printf("\n"); +#ifdef REG_VERSIONID + if ( regerror(REG_VERSIONID, NiL, pat, sizeof(pat)) > 0 ) + s = pat; + else +#endif +#ifdef REG_TEST_VERSION + s = REG_TEST_VERSION; +#else + s = "regex"; +#endif + printf("NOTE\t%s\n", s); + if ( elementsof(unsupported) > 1 ) { +#if ( REG_TEST_DEFAULT & (REG_AUGMENTED | REG_EXTENDED | REG_SHELL) ) || ! defined(REG_EXTENDED) + i = 0; +#else + i = REG_EXTENDED != 0; +#endif + for ( got = 0; i < elementsof(unsupported) - 1; i++ ) { + if ( ! got ) { + got = 1; + printf("NOTE\tunsupported: %s", unsupported[i]); + } + else + printf(",%s", unsupported[i]); + } + if ( got ) + printf("\n"); + } + } +#ifdef REG_DISCIPLINE + state.disc.disc.re_version = REG_VERSION; + state.disc.disc.re_compf = compf; + state.disc.disc.re_execf = execf; + if ( ! (state.disc.sp = sfstropen()) ) + bad("out of space [discipline string stream]\n", NiL, NiL, 0, 0); + preg.re_disc = &state.disc.disc; +#endif + if ( test & TEST_CATCH ) { + signal(SIGALRM, gotcha); + signal(SIGBUS, gotcha); + signal(SIGSEGV, gotcha); + } + while ( p == mygetline(fp) ) { + /* parse: */ + + line = p; + if ( *p == ':' && ! isspace(*(p + 1)) ) { + while ( *++p && *p != ':' ) + ; + if ( ! *p++ ) { + if ( test & TEST_BASELINE ) + printf("%s\n", line); + continue; + } + } + while ( isspace(*p) ) + p++; + if ( *p == 0 || *p == '#' || *p == 'T' ) { + if ( test & TEST_BASELINE ) + printf("%s\n", line); + continue; + } + if ( *p == ':' || *p == 'N' ) { + if ( test & TEST_BASELINE ) + printf("%s\n", line); + else if ( ! (test & (TEST_ACTUAL | TEST_FAIL | TEST_PASS | TEST_SUMMARY)) ) { + while ( *++p && ! isspace(*p) ) + ; + while ( isspace(*p) ) + p++; + printf("NOTE %s\n", p); + } + continue; + } + j = 0; + i = 0; + field[i++] = p; + for ( ;; ) { + switch ( *p++ ) { + case 0: + p--; + j = 0; + goto checkfield; + case '\t': + *(delim[i] = p - 1) = 0; + j = 1; + checkfield: + s = field[i - 1]; + if ( streq(s, "NIL") ) + field[i - 1] = 0; + else if ( streq(s, "NULL") ) + *s = 0; + while ( *p == '\t' ) { + p++; + j++; + } + tabs[i - 1] = j; + if ( ! *p ) + break; + if ( i >= elementsof(field) ) + bad("too many fields\n", NiL, NiL, 0, 0); + field[i++] = p; + /*FALLTHROUGH*/ + default: + continue; + } + break; + } + if ( ! (spec = field[0]) ) + bad("NIL spec\n", NiL, NiL, 0, 0); + + /* interpret: */ + + cflags = REG_TEST_DEFAULT; + eflags = REG_EXEC_DEFAULT; + test &= TEST_GLOBAL; + state.extracted = 0; + nmatch = 20; + nsub = -1; + for ( p = spec; *p; p++ ) { + if ( isdigit(*p) ) { + nmatch = strtol(p, &p, 10); + if ( nmatch >= elementsof(match) ) + bad("nmatch must be < 100\n", NiL, NiL, 0, 0); + p--; + continue; + } + switch ( *p ) { + case 'A': + test |= TEST_ARE; + continue; + case 'B': + // test |= TEST_BRE; + continue; + case 'C': + if ( ! (test & TEST_QUERY) && ! (skip & level) ) + bad("locale must be nested\n", NiL, NiL, 0, 0); + test &= ~TEST_QUERY; + if ( locale ) + bad("locale nesting not supported\n", NiL, NiL, 0, 0); + if ( i != 2 ) + bad("locale field expected\n", NiL, NiL, 0, 0); + if ( ! (skip & level) ) { +#if defined(LC_COLLATE) && defined(LC_CTYPE) + s = field[1]; + if ( ! s || streq(s, "POSIX") ) + s = "C"; + if ( ! (ans = setlocale(LC_COLLATE, s)) || streq(ans, "C") || + streq(ans, "POSIX") || ! (ans = setlocale(LC_CTYPE, s)) || + streq(ans, "C") || streq(ans, "POSIX") ) + skip = note(level, s, skip, test); + else { + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | + TEST_SUMMARY)) ) + printf("NOTE \"%s\" locale\n", s); + locale = level; + } +#else + skip = note(level, skip, test, "locales not supported"); +#endif + } + cflags = NOTEST; + continue; + case 'E': + test |= TEST_ERE; + continue; + case 'K': + test |= TEST_KRE; + continue; + case 'L': + test |= TEST_LRE; + continue; + case 'S': + test |= TEST_SRE; + continue; + + case 'a': + cflags |= REG_LEFT | REG_RIGHT; + continue; + case 'b': + eflags |= REG_NOTBOL; + continue; + case 'c': + cflags |= REG_COMMENT; + continue; + case 'd': + cflags |= REG_SHELL_DOT; + continue; + case 'e': + eflags |= REG_NOTEOL; + continue; + case 'f': + cflags |= REG_MULTIPLE; + continue; + case 'g': + cflags |= NOTEST; + continue; + case 'h': + cflags |= REG_MULTIREF; + continue; + case 'i': + cflags |= REG_ICASE; + continue; + case 'j': + cflags |= REG_SPAN; + continue; + case 'k': + cflags |= REG_ESCAPE; + continue; + case 'l': + cflags |= REG_LEFT; + continue; + case 'm': + cflags |= REG_MINIMAL; + continue; + case 'n': + cflags |= REG_NEWLINE; + continue; + case 'o': + cflags |= REG_SHELL_GROUP; + continue; + case 'p': + cflags |= REG_SHELL_PATH; + continue; + case 'q': + cflags |= REG_DELIMITED; + continue; + case 'r': + cflags |= REG_RIGHT; + continue; + case 's': + cflags |= REG_SHELL_ESCAPED; + continue; + case 't': + cflags |= REG_MUSTDELIM; + continue; + case 'u': + test |= TEST_UNSPECIFIED; + continue; + case 'w': + cflags |= REG_NOSUB; + continue; + case 'x': + if ( REG_LENIENT ) + cflags |= REG_LENIENT; + else + test |= TEST_LENIENT; + continue; + case 'y': + eflags |= REG_LEFT; + continue; + case 'z': + cflags |= REG_NULL; + continue; + case '$': + test |= TEST_EXPAND; + continue; + + case '/': + test |= TEST_SUB; + continue; + + case '=': + test |= TEST_DECOMP; + continue; + + case '?': + test |= TEST_VERIFY; + test &= ~(TEST_AND | TEST_OR); + state.verify = state.passed; + continue; + case '&': + test |= TEST_VERIFY | TEST_AND; + test &= ~TEST_OR; + continue; + case '|': + test |= TEST_VERIFY | TEST_OR; + test &= ~TEST_AND; + continue; + case ';': + test |= TEST_OR; + test &= ~TEST_AND; + continue; + + case '{': + level <<= 1; + if ( skip & (level >> 1) ) { + skip |= level; + cflags = NOTEST; + } + else { + skip &= ~level; + test |= TEST_QUERY; + } + continue; + case '}': + if ( level == 1 ) + bad("invalid {...} nesting\n", NiL, NiL, 0, 0); + if ( (skip & level) && ! (skip & (level >> 1)) ) { + if ( ! (test & (TEST_BASELINE | TEST_SUMMARY)) ) { + if ( test & (TEST_ACTUAL | TEST_FAIL) ) + printf("}\n"); + else if ( ! (test & TEST_PASS) ) + printf("-%d\n", state.lineno); + } + } +#if defined(LC_COLLATE) && defined(LC_CTYPE) + else if ( locale & level ) { + locale = 0; + if ( ! (skip & level) ) { + s = "C"; + setlocale(LC_COLLATE, s); + setlocale(LC_CTYPE, s); + if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | + TEST_SUMMARY)) ) + printf("NOTE \"%s\" locale\n", s); + else if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_PASS) ) + printf("}\n"); + } + else if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL) ) + printf("}\n"); + } +#endif + level >>= 1; + cflags = NOTEST; + continue; + + default: + bad("bad spec\n", spec, NiL, 0, test); + break; + } + break; + } + if ( (cflags | eflags) == NOTEST || ((skip & level) && (test & TEST_BASELINE)) ) { + if ( test & TEST_BASELINE ) { + while ( i > 1 ) + *delim[--i] = '\t'; + printf("%s\n", line); + } + continue; + } + if ( test & TEST_OR ) { + if ( ! (test & TEST_VERIFY) ) { + test &= ~TEST_OR; + if ( state.passed == state.verify && i > 1 ) + printf("NOTE\t%s\n", field[1]); + continue; + } + if ( state.passed > state.verify ) + continue; + } + else if ( test & TEST_AND ) { + if ( state.passed == state.verify ) + continue; + state.passed = state.verify; + } + if ( i < ((test & TEST_DECOMP) ? 3 : 4) ) + bad("too few fields\n", NiL, NiL, 0, test); + while ( i < elementsof(field) ) + field[i++] = 0; + if ( re == field[1] ) { + if ( streq(re, "SAME") ) { + re = ppat; + test |= TEST_SAME; + } + else { + if ( test & TEST_EXPAND ) + escape(re); + strcpy(ppat = pat, re); + } + } + else + ppat = 0; + nstr = -1; + if ( (s = field[2]) && (test & TEST_EXPAND) ) { + nstr = escape(s); +#if _REG_nexec + if ( nstr != strlen(s) ) + nexec = nstr; +#endif + } + if ( ! (ans = field[(test & TEST_DECOMP) ? 2 : 3]) ) + bad("NIL answer\n", NiL, NiL, 0, test); + msg = field[4]; + fflush(stdout); + if ( test & TEST_SUB ) +#if _REG_subcomp + cflags |= REG_DELIMITED; +#else + continue; +#endif +#if ! _REG_decomp + if ( test & TEST_DECOMP ) + continue; +#endif + + compile: + + if ( state.extracted || (skip & level) ) + continue; +#if ! (REG_TEST_DEFAULT & (REG_AUGMENTED | REG_EXTENDED | REG_SHELL)) +#ifdef REG_EXTENDED + if ( REG_EXTENDED != 0 && (test & TEST_BRE) ) +#else + if ( test & TEST_BRE ) +#endif + { + test &= ~TEST_BRE; + flags = cflags; + state.which = "BRE"; + } + else +#endif +#ifdef REG_EXTENDED + if ( test & TEST_ERE ) { + test &= ~TEST_ERE; + flags = cflags | REG_EXTENDED; + state.which = "ERE"; + } + else +#endif +#ifdef REG_AUGMENTED + if ( test & TEST_ARE ) { + test &= ~TEST_ARE; + flags = cflags | REG_AUGMENTED; + state.which = "ARE"; + } + else +#endif +#ifdef REG_LITERAL + if ( test & TEST_LRE ) { + test &= ~TEST_LRE; + flags = cflags | REG_LITERAL; + state.which = "LRE"; + } + else +#endif +#ifdef REG_SHELL + if ( test & TEST_SRE ) { + test &= ~TEST_SRE; + flags = cflags | REG_SHELL; + state.which = "SRE"; + } + else +#ifdef REG_AUGMENTED + if ( test & TEST_KRE ) { + test &= ~TEST_KRE; + flags = cflags | REG_SHELL | REG_AUGMENTED; + state.which = "KRE"; + } + else +#endif +#endif + { + if ( test & (TEST_BASELINE | TEST_PASS | TEST_VERIFY) ) + extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, + test | TEST_OK); + continue; + } + if ( (test & (TEST_QUERY | TEST_VERBOSE | TEST_VERIFY)) == TEST_VERBOSE ) { + printf("test %-3d %s ", state.lineno, state.which); + quote(re, -1, test | TEST_DELIMIT); + printf(" "); + quote(s, nstr, test | TEST_DELIMIT); + printf("\n"); + } + + nosub: + fun = "regcomp"; +#if _REG_nexec + if ( nstr >= 0 && nstr != strlen(s) ) + nexec = nstr; + + else +#endif + nexec = -1; + if ( state.extracted || (skip & level) ) + continue; + if ( ! (test & TEST_QUERY) ) + testno++; +#ifdef REG_DISCIPLINE + if ( state.stack ) + stkset(stkstd, state.stack, 0); + flags |= REG_DISCIPLINE; + state.disc.ordinal = 0; + sfstrseek(state.disc.sp, 0, SEEK_SET); +#endif + if ( ! (test & TEST_CATCH) ) + cret = regcomp(&preg, re, flags | REG_LAZY); + else if ( ! (cret = setjmp(state.gotcha)) ) { + alarm(HUNG); + cret = regcomp(&preg, re, flags | REG_LAZY); + alarm(0); + } +#if _REG_subcomp + if ( ! cret && (test & TEST_SUB) ) { + fun = "regsubcomp"; + p = re + preg.re_npat; + if ( ! (test & TEST_CATCH) ) + cret = regsubcomp(&preg, p, NiL, 0, 0); + else if ( ! (cret = setjmp(state.gotcha)) ) { + alarm(HUNG); + cret = regsubcomp(&preg, p, NiL, 0, 0); + alarm(0); + } + if ( ! cret && *(p += preg.re_npat) && ! (preg.re_sub->re_flags & REG_SUB_LAST) ) { + if ( catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, + level, test) ) + continue; + cret = REG_EFLAGS; + } + } +#endif +#if _REG_decomp + if ( ! cret && (test & TEST_DECOMP) ) { + char buf[128]; + + if ( (j = nmatch) > sizeof(buf) ) + j = sizeof(buf); + fun = "regdecomp"; + p = re + preg.re_npat; + if ( ! (test & TEST_CATCH) ) + i = regdecomp(&preg, -1, buf, j); + else if ( ! (cret = setjmp(state.gotcha)) ) { + alarm(HUNG); + i = regdecomp(&preg, -1, buf, j); + alarm(0); + } + if ( ! cret ) { + catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, + level, test); + if ( i > j ) { + if ( i != (strlen(ans) + 1) ) { + report("failed", fun, re, s, nstr, msg, flags, test); + printf(" %d byte buffer supplied, %d byte buffer required\n", j, i); + } + } + else if ( strcmp(buf, ans) ) { + report("failed", fun, re, s, nstr, msg, flags, test); + quote(ans, -1, test | TEST_DELIMIT); + printf(" expected, "); + quote(buf, -1, test | TEST_DELIMIT); + printf(" returned\n"); + } + continue; + } + } +#endif + if ( ! cret ) { + if ( ! (flags & REG_NOSUB) && nsub < 0 && *ans == '(' ) { + for ( p = ans; *p; p++ ) + if ( *p == '(' ) + nsub++; + else if ( *p == '{' ) + nsub--; + if ( nsub >= 0 ) { + if ( test & TEST_IGNORE_OVER ) { + if ( nmatch > nsub ) + nmatch = nsub + 1; + } + else if ( nsub != preg.re_nsub ) { + if ( nsub > preg.re_nsub ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | + TEST_QUERY | TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, + skip, level, test | TEST_DELIMIT); + else { + report("re_nsub incorrect", fun, re, NiL, -1, msg, flags, test); + printf("at least %d expected, %zu returned\n", nsub, + preg.re_nsub); + state.errors++; + } + } + else + nsub = preg.re_nsub; + } + } + } + if ( ! (test & (TEST_DECOMP | TEST_SUB)) && *ans && *ans != '(' && + ! streq(ans, "OK") && ! streq(ans, "NOMATCH") ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, "OK", NiL, 0, 0, skip, level, + test | TEST_DELIMIT); + else if ( ! (test & TEST_LENIENT) ) { + report("failed", fun, re, NiL, -1, msg, flags, test); + printf("%s expected, OK returned\n", ans); + } + catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, + level, test); + continue; + } + } + else { + if ( test & TEST_LENIENT ) + /* we'll let it go this time */; + else if ( ! *ans || ans[0] == '(' || (cret == REG_BADPAT && streq(ans, "NOMATCH")) ) { + got = 0; + for ( i = 1; i < elementsof(codes); i++ ) + if ( cret == codes[i].code ) + got = i; + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, + skip, level, test | TEST_DELIMIT); + else { + report("failed", fun, re, NiL, -1, msg, flags, test); + printf("%s returned: ", codes[got].name); + error(&preg, cret); + } + } + else { + expected = got = 0; + for ( i = 1; i < elementsof(codes); i++ ) { + if ( streq(ans, codes[i].name) ) + expected = i; + if ( cret == codes[i].code ) + got = i; + } + if ( ! expected ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | + TEST_QUERY | TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, + skip, level, test | TEST_DELIMIT); + else { + report("failed: invalid error code", NiL, re, NiL, -1, msg, flags, + test); + printf("%s expected, %s returned\n", ans, codes[got].name); + } + } + else if ( cret != codes[expected].code && cret != REG_BADPAT ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | + TEST_QUERY | TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, codes[got].name, NiL, 0, 0, + skip, level, test | TEST_DELIMIT); + else if ( test & TEST_IGNORE_ERROR ) + state.ignored++; + else { + report("should fail and did", fun, re, NiL, -1, msg, flags, test); + printf("%s expected, %s returned: ", ans, codes[got].name); + state.errors--; + state.warnings++; + error(&preg, cret); + } + } + } + goto compile; + } + +#if _REG_nexec + execute: + if ( nexec >= 0 ) + fun = "regnexec"; + else +#endif + fun = "regexec"; + + for ( i = 0; i < elementsof(match); i++ ) + match[i] = state.NOMATCH; + +#if _REG_nexec + if ( nexec >= 0 ) { + eret = regnexec(&preg, s, nexec, nmatch, match, eflags); + s[nexec] = 0; + } + else +#endif + { + if ( ! (test & TEST_CATCH) ) + eret = regexec(&preg, s, nmatch, match, eflags); + else if ( ! (eret = setjmp(state.gotcha)) ) { + alarm(HUNG); + eret = regexec(&preg, s, nmatch, match, eflags); + alarm(0); + } + } +#if _REG_subcomp + if ( (test & TEST_SUB) && ! eret ) { + fun = "regsubexec"; + if ( ! (test & TEST_CATCH) ) + eret = regsubexec(&preg, s, nmatch, match); + else if ( ! (eret = setjmp(state.gotcha)) ) { + alarm(HUNG); + eret = regsubexec(&preg, s, nmatch, match); + alarm(0); + } + } +#endif + if ( flags & REG_NOSUB ) { + if ( eret ) { + if ( eret != REG_NOMATCH || ! streq(ans, "NOMATCH") ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | + TEST_QUERY | TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, 0, skip, + level, test | TEST_DELIMIT); + else { + report("REG_NOSUB failed", fun, re, s, nstr, msg, flags, test); + error(&preg, eret); + } + } + } + else if ( streq(ans, "NOMATCH") ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, + level, test | TEST_DELIMIT); + else { + report("should fail and didn't", fun, re, s, nstr, msg, flags, test); + error(&preg, eret); + } + } + } + else if ( eret ) { + if ( eret != REG_NOMATCH || ! streq(ans, "NOMATCH") ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, "NOMATCH", NiL, 0, nsub, skip, + level, test | TEST_DELIMIT); + else { + report("failed", fun, re, s, nstr, msg, flags, test); + if ( eret != REG_NOMATCH ) + error(&preg, eret); + else if ( *ans ) + printf("expected: %s\n", ans); + else + printf("\n"); + } + } + } + else if ( streq(ans, "NOMATCH") ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, + level, test | TEST_DELIMIT); + else { + report("should fail and didn't", fun, re, s, nstr, msg, flags, test); + matchprint(match, nmatch, nsub, NiL, test); + } + } +#if _REG_subcomp + else if ( test & TEST_SUB ) { + p = preg.re_sub->re_buf; + if ( strcmp(p, ans) ) { + report("failed", fun, re, s, nstr, msg, flags, test); + quote(ans, -1, test | TEST_DELIMIT); + printf(" expected, "); + quote(p, -1, test | TEST_DELIMIT); + printf(" returned\n"); + } + } +#endif + else if ( ! *ans ) { + if ( match[0].rm_so != state.NOMATCH.rm_so ) { + if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = + extract(tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, test); + else { + report("failed: no match but match array assigned", NiL, re, s, nstr, msg, + flags, test); + matchprint(match, nmatch, nsub, NiL, test); + } + } + } + else if ( matchcheck(match, nmatch, nsub, ans, re, s, nstr, flags, test) ) { +#if _REG_nexec + if ( nexec < 0 && ! nonexec ) { + nexec = nstr >= 0 ? nstr : strlen(s); + s[nexec] = '\n'; + testno++; + goto execute; + } +#endif + if ( ! (test & (TEST_DECOMP | TEST_SUB | TEST_VERIFY)) && ! nonosub ) { + if ( catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, + level, test) ) + continue; + flags |= REG_NOSUB; + goto nosub; + } + if ( test & (TEST_BASELINE | TEST_PASS | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, + level, test | TEST_OK); + } + else if ( test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS | TEST_QUERY | + TEST_SUMMARY | TEST_VERIFY) ) + skip = extract(tabs, line, re, s, ans, msg, NiL, match, nmatch, nsub, skip, level, + test | TEST_DELIMIT); + if ( catchfree(&preg, flags, tabs, line, re, s, ans, msg, NiL, NiL, 0, 0, skip, level, + test) ) + continue; + goto compile; + } + if ( test & TEST_SUMMARY ) + printf( + "tests=%-4d errors=%-4d warnings=%-2d ignored=%-2d unspecified=%-2d signals=%d\n", + testno, state.errors, state.warnings, state.ignored, state.unspecified, + state.signals); + else if ( ! (test & (TEST_ACTUAL | TEST_BASELINE | TEST_FAIL | TEST_PASS)) ) { + printf("TEST\t%s", unit); + if ( subunit ) + printf(" %-.*s", subunitlen, subunit); + printf(", %d test%s", testno, testno == 1 ? "" : "s"); + if ( state.ignored ) + printf(", %d ignored mismatche%s", state.ignored, state.ignored == 1 ? "" : "s"); + if ( state.warnings ) + printf(", %d warning%s", state.warnings, state.warnings == 1 ? "" : "s"); + if ( state.unspecified ) + printf(", %d unspecified difference%s", state.unspecified, + state.unspecified == 1 ? "" : "s"); + if ( state.signals ) + printf(", %d signal%s", state.signals, state.signals == 1 ? "" : "s"); + printf(", %d error%s\n", state.errors, state.errors == 1 ? "" : "s"); + } + if ( fp != stdin ) + fclose(fp); + } + return 0; +} diff --git a/hilti/src/3rdparty/libtask/COPYRIGHT b/hilti/src/3rdparty/libtask/COPYRIGHT new file mode 100644 index 000000000..45fcbc0e1 --- /dev/null +++ b/hilti/src/3rdparty/libtask/COPYRIGHT @@ -0,0 +1,43 @@ + +This software was developed as part of a project at MIT. + +Copyright (c) 2005-2007 Russ Cox, + Massachusetts Institute of Technology + +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. + +=== + +Contains parts of an earlier library that has: + +/* + * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox + * Copyright (c) 2003 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + diff --git a/hilti/src/3rdparty/libtask/README b/hilti/src/3rdparty/libtask/README new file mode 100644 index 000000000..f199c6eee --- /dev/null +++ b/hilti/src/3rdparty/libtask/README @@ -0,0 +1,246 @@ +Libtask is a simple coroutine library. It runs on Linux (ARM, MIPS, and x86), +FreeBSD (x86), OS X (PowerPC x86, and x86-64), and SunOS Solaris (Sparc), +and is easy to port to other systems. + +Libtask gives the programmer the illusion of threads, but +the operating system sees only a single kernel thread. +For clarity, we refer to the coroutines as "tasks," not threads. + +Scheduling is cooperative. Only one task runs at a time, +and it cannot be rescheduled without explicitly giving up +the CPU. Most of the functions provided in task.h do have +the possibility of going to sleep. Programs using the task +functions should #include . + +--- Basic task manipulation + +int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize); + + Create a new task running f(arg) on a stack of size stacksize. + +void tasksystem(void); + + Mark the current task as a "system" task. These are ignored + for the purposes of deciding the program is done running + (see taskexit next). + +void taskexit(int status); + + Exit the current task. If this is the last non-system task, + exit the entire program using the given exit status. + +void taskexitall(int status); + + Exit the entire program, using the given exit status. + +void taskmain(int argc, char *argv[]); + + Write this function instead of main. Libtask provides its own main. + +int taskyield(void); + + Explicitly give up the CPU. The current task will be scheduled + again once all the other currently-ready tasks have a chance + to run. Returns the number of other tasks that ran while the + current task was waiting. (Zero means there are no other tasks + trying to run.) + +int taskdelay(unsigned int ms) + + Explicitly give up the CPU for at least ms milliseconds. + Other tasks continue to run during this time. + +void** taskdata(void); + + Return a pointer to a single per-task void* pointer. + You can use this as a per-task storage place. + +void needstack(int n); + + Tell the task library that you need at least n bytes left + on the stack. If you don't have it, the task library will call abort. + (It's hard to figure out how big stacks should be. I usually make + them really big (say 32768) and then don't worry about it.) + +void taskname(char*, ...); + + Takes an argument list like printf. Sets the current task's name. + +char* taskgetname(void); + + Returns the current task's name. Is the actual buffer; do not free. + +void taskstate(char*, ...); +char* taskgetstate(void); + + Like taskname and taskgetname but for the task state. + + When you send a tasked program a SIGQUIT (or SIGINFO, on BSD) + it will print a list of all its tasks and their names and states. + This is useful for debugging why your program isn't doing anything! + +unsigned int taskid(void); + + Return the unique task id for the current task. + +--- Non-blocking I/O + +There is a small amount of runtime support for non-blocking I/O +on file descriptors. + +int fdnoblock(int fd); + + Sets I/O on the given fd to be non-blocking. Should be + called before any of the other fd routines. + +int fdread(int, void*, int); + + Like regular read(), but puts task to sleep while waiting for + data instead of blocking the whole program. + +int fdwrite(int, void*, int); + + Like regular write(), but puts task to sleep while waiting to + write data instead of blocking the whole program. + +void fdwait(int fd, int rw); + + Low-level call sitting underneath fdread and fdwrite. + Puts task to sleep while waiting for I/O to be possible on fd. + Rw specifies type of I/O: 'r' means read, 'w' means write, + anything else means just exceptional conditions (hang up, etc.) + The 'r' and 'w' also wake up for exceptional conditions. + +--- Network I/O + +These are convenient packaging of the ugly Unix socket routines. +They can all put the current task to sleep during the call. + +int netannounce(int proto, char *address, int port) + + Start a network listener running on address and port of protocol. + Proto is either TCP or UDP. Port is a port number. Address is a + string version of a host name or IP address. If address is null, + then announce binds to the given port on all available interfaces. + Returns a fd to use with netaccept. + Examples: netannounce(TCP, "localhost", 80) or + netannounce(TCP, "127.0.0.1", 80) or netannounce(TCP, 0, 80). + +int netaccept(int fd, char *server, int *port) + + Get the next connection that comes in to the listener fd. + Returns a fd to use to talk to the guy who just connected. + If server is not null, it must point at a buffer of at least + 16 bytes that is filled in with the remote IP address. + If port is not null, it is filled in with the report port. + Example: + char server[16]; + int port; + + if(netaccept(fd, server, &port) >= 0) + printf("connect from %s:%d", server, port); + +int netdial(int proto, char *name, int port) + + Create a new (outgoing) connection to a particular host. + Name can be an ip address or a domain name. If it's a domain name, + the entire program will block while the name is resolved + (the DNS library does not provide a nice non-blocking interface). + Example: netdial(TCP, "www.google.com", 80) + or netdial(TCP, "18.26.4.9", 80) + +--- Time + +unsigned int taskdelay(unsigned int ms) + + Put the current task to sleep for approximately ms milliseconds. + Return the actual amount of time slept, in milliseconds. + +--- Example programs + +In this directory, tcpproxy.c is a simple TCP proxy that illustrates +most of the above. You can run + + tcpproxy 1234 www.google.com 80 + +and then you should be able to visit http://localhost:1234/ and see Google. + +Other examples are: + primes.c - simple prime sieve + httpload.c - simple HTTP load generator + testdelay.c - test taskdelay() + +--- Building + +To build, run make. You can run make install to copy task.h and +libtask.a to the appropriate places in /usr/local. Then you +should be able to just link with -ltask in your programs +that use it. + +On SunOS Solaris machines, run makesun instead of just make. + +--- Contact Info + +Please email me with questions or problems. + +Russ Cox +rsc@swtch.com + + +--- Stuff you probably won't use at first --- +--- but might want to know about eventually --- + +void tasksleep(Rendez*); +int taskwakeup(Rendez*); +int taskwakeupall(Rendez*); + + A Rendez is a condition variable. You can declare a new one by + just allocating memory for it (or putting it in another structure) + and then zeroing the memory. Tasksleep(r) 'sleeps on r', giving + up the CPU. Multiple tasks can sleep on a single Rendez. + When another task comes along and calls taskwakeup(r), + the first task sleeping on r (if any) will be woken up. + Taskwakeupall(r) wakes up all the tasks sleeping on r. + They both return the actual number of tasks awakened. + + + +void qlock(QLock*); +int canqlock(QLock*); +void qunlock(QLock*); + + You probably won't need locks because of the cooperative + scheduling, but if you do, here are some. You can make a new + QLock by just declaring it and zeroing the memory. + Calling qlock will give up the CPU if the lock is held by someone else. + Calling qunlock will not give up the CPU. + Calling canqlock tries to lock the lock, but will not give up the CPU. + It returns 1 if the lock was acquired, 0 if it cannot be at this time. + +void rlock(RWLock*); +int canrlock(RWLock*); +void runlock(RWLock*); + +void wlock(RWLock*); +int canwlock(RWLock*); +void wunlock(RWLock*); + + RWLocks are reader-writer locks. Any number of readers + can lock them at once, but only one writer at a time. + If a writer is holding it, there can't be any readers. + + +Channel *chancreate(int, int); +etc. + + Channels are buffered communication pipes you can + use to send messages between tasks. Some people like + doing most of the inter-task communication using channels. + + For details on channels see the description of channels in + http://swtch.com/usr/local/plan9/man/man3/thread.html and + http://swtch.com/~rsc/thread/ + and also the example program primes.c, which implements + a concurrent prime sieve. + + diff --git a/hilti/src/3rdparty/libtask/README.bro b/hilti/src/3rdparty/libtask/README.bro new file mode 100644 index 000000000..f654f4a72 --- /dev/null +++ b/hilti/src/3rdparty/libtask/README.bro @@ -0,0 +1,4 @@ +This is a stripped down version of libtask. Bro just needs the context +switching, and parts unrelated to that have been removed. +See https://swtch.com/libtask for the complete code. + diff --git a/hilti/src/3rdparty/libtask/asm.S b/hilti/src/3rdparty/libtask/asm.S new file mode 100644 index 000000000..a57662a63 --- /dev/null +++ b/hilti/src/3rdparty/libtask/asm.S @@ -0,0 +1,326 @@ +/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ + +#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 +#define NEEDX86CONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#endif + +#if defined(__OpenBSD__) && defined(__i386__) +#define NEEDX86CONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#endif + +#if defined(__APPLE__) +#if defined(__i386__) +#define NEEDX86CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#elif defined(__x86_64__) +#define NEEDAMD64CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#else +#define NEEDPOWERCONTEXT 1 +#define SET __setmcontext +#define GET __getmcontext +#endif +#endif + +#if defined(__linux__) && defined(__arm__) +#define NEEDARMCONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#endif + +#if defined(__linux__) && defined(__mips__) +#define NEEDMIPSCONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#endif + +#ifdef NEEDX86CONTEXT +.globl SET +SET: + movl 4(%esp), %eax + + movl 8(%eax), %fs + movl 12(%eax), %es + movl 16(%eax), %ds + movl 76(%eax), %ss + movl 20(%eax), %edi + movl 24(%eax), %esi + movl 28(%eax), %ebp + movl 36(%eax), %ebx + movl 40(%eax), %edx + movl 44(%eax), %ecx + + movl 72(%eax), %esp + pushl 60(%eax) /* new %eip */ + movl 48(%eax), %eax + ret + +.globl GET +GET: + movl 4(%esp), %eax + + movl %fs, 8(%eax) + movl %es, 12(%eax) + movl %ds, 16(%eax) + movl %ss, 76(%eax) + movl %edi, 20(%eax) + movl %esi, 24(%eax) + movl %ebp, 28(%eax) + movl %ebx, 36(%eax) + movl %edx, 40(%eax) + movl %ecx, 44(%eax) + + movl $1, 48(%eax) /* %eax */ + movl (%esp), %ecx /* %eip */ + movl %ecx, 60(%eax) + leal 4(%esp), %ecx /* %esp */ + movl %ecx, 72(%eax) + + movl 44(%eax), %ecx /* restore %ecx */ + movl $0, %eax + ret +#endif + +#ifdef NEEDAMD64CONTEXT +.globl SET +SET: + movq 16(%rdi), %rsi + movq 24(%rdi), %rdx + movq 32(%rdi), %rcx + movq 40(%rdi), %r8 + movq 48(%rdi), %r9 + movq 56(%rdi), %rax + movq 64(%rdi), %rbx + movq 72(%rdi), %rbp + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + movq 184(%rdi), %rsp + pushq 160(%rdi) /* new %eip */ + movq 8(%rdi), %rdi + ret + +.globl GET +GET: + movq %rdi, 8(%rdi) + movq %rsi, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rcx, 32(%rdi) + movq %r8, 40(%rdi) + movq %r9, 48(%rdi) + movq $1, 56(%rdi) /* %rax */ + movq %rbx, 64(%rdi) + movq %rbp, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + + movq (%rsp), %rcx /* %rip */ + movq %rcx, 160(%rdi) + leaq 8(%rsp), %rcx /* %rsp */ + movq %rcx, 184(%rdi) + + movq 32(%rdi), %rcx /* restore %rcx */ + movq $0, %rax + ret +#endif + +#ifdef NEEDPOWERCONTEXT +/* get FPR and VR use flags with sc 0x7FF3 */ +/* get vsave with mfspr reg, 256 */ + +.text +.align 2 + +.globl GET +GET: /* xxx: instruction scheduling */ + mflr r0 + mfcr r5 + mfctr r6 + mfxer r7 + stw r0, 0*4(r3) + stw r5, 1*4(r3) + stw r6, 2*4(r3) + stw r7, 3*4(r3) + + stw r1, 4*4(r3) + stw r2, 5*4(r3) + li r5, 1 /* return value for setmcontext */ + stw r5, 6*4(r3) + + stw r13, (0+7)*4(r3) /* callee-save GPRs */ + stw r14, (1+7)*4(r3) /* xxx: block move */ + stw r15, (2+7)*4(r3) + stw r16, (3+7)*4(r3) + stw r17, (4+7)*4(r3) + stw r18, (5+7)*4(r3) + stw r19, (6+7)*4(r3) + stw r20, (7+7)*4(r3) + stw r21, (8+7)*4(r3) + stw r22, (9+7)*4(r3) + stw r23, (10+7)*4(r3) + stw r24, (11+7)*4(r3) + stw r25, (12+7)*4(r3) + stw r26, (13+7)*4(r3) + stw r27, (14+7)*4(r3) + stw r28, (15+7)*4(r3) + stw r29, (16+7)*4(r3) + stw r30, (17+7)*4(r3) + stw r31, (18+7)*4(r3) + + li r3, 0 /* return */ + blr + +.globl SET +SET: + lwz r13, (0+7)*4(r3) /* callee-save GPRs */ + lwz r14, (1+7)*4(r3) /* xxx: block move */ + lwz r15, (2+7)*4(r3) + lwz r16, (3+7)*4(r3) + lwz r17, (4+7)*4(r3) + lwz r18, (5+7)*4(r3) + lwz r19, (6+7)*4(r3) + lwz r20, (7+7)*4(r3) + lwz r21, (8+7)*4(r3) + lwz r22, (9+7)*4(r3) + lwz r23, (10+7)*4(r3) + lwz r24, (11+7)*4(r3) + lwz r25, (12+7)*4(r3) + lwz r26, (13+7)*4(r3) + lwz r27, (14+7)*4(r3) + lwz r28, (15+7)*4(r3) + lwz r29, (16+7)*4(r3) + lwz r30, (17+7)*4(r3) + lwz r31, (18+7)*4(r3) + + lwz r1, 4*4(r3) + lwz r2, 5*4(r3) + + lwz r0, 0*4(r3) + mtlr r0 + lwz r0, 1*4(r3) + mtcr r0 /* mtcrf 0xFF, r0 */ + lwz r0, 2*4(r3) + mtctr r0 + lwz r0, 3*4(r3) + mtxer r0 + + lwz r3, 6*4(r3) + blr +#endif + +#ifdef NEEDARMCONTEXT +.globl GET +GET: + str r1, [r0,#4] + str r2, [r0,#8] + str r3, [r0,#12] + str r4, [r0,#16] + str r5, [r0,#20] + str r6, [r0,#24] + str r7, [r0,#28] + str r8, [r0,#32] + str r9, [r0,#36] + str r10, [r0,#40] + str r11, [r0,#44] + str r12, [r0,#48] + str r13, [r0,#52] + str r14, [r0,#56] + /* store 1 as r0-to-restore */ + mov r1, #1 + str r1, [r0] + /* return 0 */ + mov r0, #0 + mov pc, lr + +.globl SET +SET: + ldr r1, [r0,#4] + ldr r2, [r0,#8] + ldr r3, [r0,#12] + ldr r4, [r0,#16] + ldr r5, [r0,#20] + ldr r6, [r0,#24] + ldr r7, [r0,#28] + ldr r8, [r0,#32] + ldr r9, [r0,#36] + ldr r10, [r0,#40] + ldr r11, [r0,#44] + ldr r12, [r0,#48] + ldr r13, [r0,#52] + ldr r14, [r0,#56] + ldr r0, [r0] + mov pc, lr +#endif + +#ifdef NEEDMIPSCONTEXT +.globl GET +GET: + sw $4, 24($4) + sw $5, 28($4) + sw $6, 32($4) + sw $7, 36($4) + + sw $16, 72($4) + sw $17, 76($4) + sw $18, 80($4) + sw $19, 84($4) + sw $20, 88($4) + sw $21, 92($4) + sw $22, 96($4) + sw $23, 100($4) + + sw $28, 120($4) /* gp */ + sw $29, 124($4) /* sp */ + sw $30, 128($4) /* fp */ + sw $31, 132($4) /* ra */ + + xor $2, $2, $2 + j $31 + nop + +.globl SET +SET: + lw $16, 72($4) + lw $17, 76($4) + lw $18, 80($4) + lw $19, 84($4) + lw $20, 88($4) + lw $21, 92($4) + lw $22, 96($4) + lw $23, 100($4) + + lw $28, 120($4) /* gp */ + lw $29, 124($4) /* sp */ + lw $30, 128($4) /* fp */ + + /* + * If we set $31 directly and j $31, + * we would loose the outer return address. + * Use a temporary register, then. + */ + lw $8, 132($4) /* ra */ + + /* bug: not setting the pc causes a bus error */ + lw $25, 132($4) /* pc */ + + lw $5, 28($4) + lw $6, 32($4) + lw $7, 36($4) + lw $4, 24($4) + + j $8 + nop +#endif diff --git a/hilti/src/3rdparty/libtask/context.c b/hilti/src/3rdparty/libtask/context.c new file mode 100644 index 000000000..27d1b0d52 --- /dev/null +++ b/hilti/src/3rdparty/libtask/context.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ + +#include "taskimpl.h" + +#if defined(__APPLE__) +#if defined(__i386__) +#define NEEDX86MAKECONTEXT +#define NEEDSWAPCONTEXT +#elif defined(__x86_64__) +#define NEEDAMD64MAKECONTEXT +#define NEEDSWAPCONTEXT +#else +#define NEEDPOWERMAKECONTEXT +#define NEEDSWAPCONTEXT +#endif +#endif + +#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 +#define NEEDX86MAKECONTEXT +#define NEEDSWAPCONTEXT +#endif + +#if defined(__OpenBSD__) && defined(__i386__) +#define NEEDX86MAKECONTEXT +#define NEEDSWAPCONTEXT +#endif + +#if defined(__linux__) && defined(__arm__) +#define NEEDSWAPCONTEXT +#define NEEDARMMAKECONTEXT +#endif + +#if defined(__linux__) && defined(__mips__) +#define NEEDSWAPCONTEXT +#define NEEDMIPSMAKECONTEXT +#endif + +#ifdef NEEDPOWERMAKECONTEXT +void +makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) +{ + ulong *sp, *tos; + va_list arg; + + tos = (ulong*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(ulong); + sp = tos - 16; + ucp->mc.pc = (long)func; + ucp->mc.sp = (long)sp; + va_start(arg, argc); + ucp->mc.r3 = va_arg(arg, long); + va_end(arg); +} +#endif + +#ifdef NEEDX86MAKECONTEXT +void +makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) +{ + int *sp; + + sp = (int*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/4; + sp -= argc; + sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ + memmove(sp, &argc+1, argc*sizeof(int)); + + *--sp = 0; /* return address */ + ucp->uc_mcontext.mc_eip = (long)func; + ucp->uc_mcontext.mc_esp = (int)sp; +} +#endif + +#ifdef NEEDAMD64MAKECONTEXT +void +makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...) +{ + long *sp; + va_list va; + + memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext); + if(argc != 2) + __builtin_trap(); // -Robin + va_start(va, argc); + ucp->uc_mcontext.mc_rdi = va_arg(va, int); + ucp->uc_mcontext.mc_rsi = va_arg(va, int); + va_end(va); + sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long); + sp -= argc; + sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ + *--sp = 0; /* return address */ + ucp->uc_mcontext.mc_rip = (long)func; + ucp->uc_mcontext.mc_rsp = (long)sp; +} +#endif + +#ifdef NEEDARMMAKECONTEXT +void +makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) +{ + int i, *sp; + va_list arg; + + sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; + va_start(arg, argc); + for(i=0; i<4 && iuc_mcontext.gregs[i] = va_arg(arg, uint); + va_end(arg); + uc->uc_mcontext.gregs[13] = (uint)sp; + uc->uc_mcontext.gregs[14] = (uint)fn; +} +#endif + +#ifdef NEEDMIPSMAKECONTEXT +void +makecontext(ucontext_t *uc, void (*fn)(void), int argc, ...) +{ + int i, *sp; + va_list arg; + + va_start(arg, argc); + sp = (int*)uc->uc_stack.ss_sp+uc->uc_stack.ss_size/4; + for(i=0; i<4 && iuc_mcontext.mc_regs[i+4] = va_arg(arg, int); + va_end(arg); + uc->uc_mcontext.mc_regs[29] = (int)sp; + uc->uc_mcontext.mc_regs[31] = (int)fn; +} +#endif + +#ifdef NEEDSWAPCONTEXT +int +swapcontext(ucontext_t *oucp, const ucontext_t *ucp) +{ + if(getcontext(oucp) == 0) + setcontext(ucp); + return 0; +} +#endif + diff --git a/hilti/src/3rdparty/pathfind/.gitignore b/hilti/src/3rdparty/pathfind/.gitignore new file mode 100644 index 000000000..5ac365fcc --- /dev/null +++ b/hilti/src/3rdparty/pathfind/.gitignore @@ -0,0 +1,20 @@ +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so + +# Compiled Static libraries +*.lai +*.la +*.a + +# Temporary files from editors +*~ + +# Various source locations: +.cproject +.project +.settings/ diff --git a/hilti/src/3rdparty/pathfind/CMakeLists.txt b/hilti/src/3rdparty/pathfind/CMakeLists.txt new file mode 100644 index 000000000..95d0e55b9 --- /dev/null +++ b/hilti/src/3rdparty/pathfind/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.8) +project(pathfind) + +#set(CMAKE_BUILD_TYPE Release) +#set(CMAKE_BUILD_TYPE Debug) + +SET(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib) + +# Call CMake on the subdirectories +add_subdirectory(src) diff --git a/hilti/src/3rdparty/pathfind/COPYING b/hilti/src/3rdparty/pathfind/COPYING new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/hilti/src/3rdparty/pathfind/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/hilti/src/3rdparty/pathfind/COPYING.LESSER b/hilti/src/3rdparty/pathfind/COPYING.LESSER new file mode 100644 index 000000000..65c5ca88a --- /dev/null +++ b/hilti/src/3rdparty/pathfind/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/hilti/src/3rdparty/pathfind/README.hilti b/hilti/src/3rdparty/pathfind/README.hilti new file mode 100644 index 000000000..90e34f22e --- /dev/null +++ b/hilti/src/3rdparty/pathfind/README.hilti @@ -0,0 +1 @@ +From https://github.com/bkloppenborg/pathfind diff --git a/hilti/src/3rdparty/pathfind/README.md b/hilti/src/3rdparty/pathfind/README.md new file mode 100644 index 000000000..26272618e --- /dev/null +++ b/hilti/src/3rdparty/pathfind/README.md @@ -0,0 +1,4 @@ +pathfinding +=========== + +A C++ library for finding the path to the current executable. diff --git a/hilti/src/3rdparty/pathfind/build/.gitignore b/hilti/src/3rdparty/pathfind/build/.gitignore new file mode 100644 index 000000000..f1437d5e3 --- /dev/null +++ b/hilti/src/3rdparty/pathfind/build/.gitignore @@ -0,0 +1,6 @@ +# Ignore everything +* + +# Except the .gitignore +!.gitignore + diff --git a/hilti/src/3rdparty/pathfind/src/CMakeLists.txt b/hilti/src/3rdparty/pathfind/src/CMakeLists.txt new file mode 100644 index 000000000..0fcf2ff79 --- /dev/null +++ b/hilti/src/3rdparty/pathfind/src/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright (c) 2012 Brian Kloppenborg +# +# This file is part of the Path Finding Library (PathFind). +# +# PathFind 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 3 +# of the License, or (at your option) any later version. +# +# PathFind 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 LIBOI. If not, see . + + +cmake_minimum_required(VERSION 2.8) +project(pathfind) + +file(GLOB SOURCE *.cpp ) + +add_library(pathfind SHARED EXCLUDE_FROM_ALL ${SOURCE}) + +add_library(pathfind_static STATIC ${SOURCE}) + +install(TARGETS pathfind_static DESTINATION lib) +install(FILES pathfind.hpp DESTINATION include) diff --git a/hilti/src/3rdparty/pathfind/src/PathFind.cpp b/hilti/src/3rdparty/pathfind/src/PathFind.cpp new file mode 100644 index 000000000..f45654743 --- /dev/null +++ b/hilti/src/3rdparty/pathfind/src/PathFind.cpp @@ -0,0 +1,98 @@ +/* + * PathFinding.cpp + * + * Created on: Oct. 25, 2011 + * Author: bkloppenborg + */ + +/* + * Copyright (c) 2012 Brian Kloppenborg + * + * This file is part of the Path Finding Library (PathFind). + * + * PathFind 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 3 + * of the License, or (at your option) any later version. + * + * PathFind 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 LIBOI. If not, see . + */ + +// [HILTI] Added include +#include + +#include "PathFind.hpp" + +/// Find the path of the current executable using GetModuleFileNameW (Windows) +string do_GetModuleFileNameW() +{ +#if defined (WIN32) // Windows + HMODULE hModule = GetModuleHandleW(NULL); + WCHAR path[MAX_PATH]; + GetModuleFileNameW(hModule, path, MAX_PATH); +#else + string path = ""; +#endif + + return string(path); +} + +/// Find the path of the current executable using _NSGetExecutablePath (Apple/Mac) +string do_NSGetExecutablePath() +{ +#if defined (__APPLE__) || defined(MACOSX) // Apple / OSX + char path[1024]; + uint32_t size = sizeof(path); +#if 0 // [Robin] Removed the prints. + if (_NSGetExecutablePath(path, &size) == 0) + printf("executable path is %s\n", path); + else + printf("buffer too small; need size %u\n", size); +#else + _NSGetExecutablePath(path, &size); +#endif + +#else + string path = ""; +#endif + + return string(path); +} + +/// Find the path of the current executable using do_readlink (BSD, Solaris, Linux) +// [HILTI] Updated implementation. +string do_readlink(std::string const& path) +{ + char buf[1024]; + if ( auto len = ::readlink(path.c_str(), buf, sizeof(buf)-1); len >= 0 ) { + buf[len] = '\0'; + return string(buf); + } + else + return path; +} + +string FindExecutable() +{ + string path; + +#if defined (__APPLE__) || defined(MACOSX) // Apple / OSX + path = do_NSGetExecutablePath(); +#elif defined (WIN32) // Windows + path = do_GetModuleFileNameW(); +#elif defined (BSD) // BSD variants + path = do_readlink("/proc/curproc/file"); +#elif defined (sun) || defined(__sun) // Solaris + path = do_readlink("/proc/self/path/a.out"); +#elif defined (__gnu_linux__) // Linux + path = do_readlink("/proc/self/exe"); +#endif + + return path; +} diff --git a/hilti/src/3rdparty/pathfind/src/PathFind.hpp b/hilti/src/3rdparty/pathfind/src/PathFind.hpp new file mode 100644 index 000000000..812368738 --- /dev/null +++ b/hilti/src/3rdparty/pathfind/src/PathFind.hpp @@ -0,0 +1,41 @@ +/* + * PathFinding.h + * + * Created on: Oct. 25, 2011 + * Author: bkloppenborg + */ + +/* + * Copyright (c) 2012 Brian Kloppenborg + * + * This file is part of the Path Finding Library (PathFind). + * + * PathFind 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 3 + * of the License, or (at your option) any later version. + * + * PathFind 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 LIBOI. If not, see . + */ + +#if defined (__APPLE__) || defined(MACOSX) // Apple +#include +#elif defined (WIN32) // Windows +// No includes necessary? +#elif defined (BSD) || defined(__gnu_linux__) || defined(sun) || defined(__sun) // BSD, Linux, Solaris +#include +#endif + +#include +using namespace std; + +string do_GetModuleFileNameW(); +string do_NSGetExecutablePath(); +string do_readlink(std::string const& path); +string FindExecutable(); diff --git a/hilti/src/3rdparty/utf8proc/LICENSE.md b/hilti/src/3rdparty/utf8proc/LICENSE.md new file mode 100644 index 000000000..c7243cfaf --- /dev/null +++ b/hilti/src/3rdparty/utf8proc/LICENSE.md @@ -0,0 +1,93 @@ +## utf8proc license ## + +**utf8proc** is a software package originally developed +by Jan Behrens and the rest of the Public Software Group, who +deserve nearly all of the credit for this library, that is now maintained by the Julia-language developers. Like the original utf8proc, +whose copyright and license statements are reproduced below, all new +work on the utf8proc library is licensed under the [MIT "expat" +license](http://opensource.org/licenses/MIT): + +*Copyright © 2014-2019 by Steven G. Johnson, Jiahao Chen, Tony Kelman, Jonas Fonseca, and other contributors listed in the git history.* + +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. + +## Original utf8proc license ## + +*Copyright (c) 2009, 2013 Public Software Group e. V., Berlin, Germany* + +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. + +## Unicode data license ## + +This software distribution contains derived data from a modified version of +the Unicode data files. The following license applies to that data: + +**COPYRIGHT AND PERMISSION NOTICE** + +*Copyright (c) 1991-2007 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html.* + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Unicode data files and any associated documentation (the "Data +Files") or Unicode software and any associated documentation (the +"Software") to deal in the Data Files or Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, and/or sell copies of the Data Files or Software, and +to permit persons to whom the Data Files or Software are furnished to do +so, provided that (a) the above copyright notice(s) and this permission +notice appear with all copies of the Data Files or Software, (b) both the +above copyright notice(s) and this permission notice appear in associated +documentation, and (c) there is clear notice in each modified Data File or +in the Software as well as in the documentation associated with the Data +File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE 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 OF +THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR +CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be +registered in some jurisdictions. All other trademarks and registered +trademarks mentioned herein are the property of their respective owners. diff --git a/hilti/src/3rdparty/utf8proc/README.hilti b/hilti/src/3rdparty/utf8proc/README.hilti new file mode 100644 index 000000000..8cec7167c --- /dev/null +++ b/hilti/src/3rdparty/utf8proc/README.hilti @@ -0,0 +1 @@ +These files are copied from https://github.com/JuliaStrings/utf8proc. diff --git a/hilti/src/3rdparty/utf8proc/utf8proc.c b/hilti/src/3rdparty/utf8proc/utf8proc.c new file mode 100644 index 000000000..297c1dc58 --- /dev/null +++ b/hilti/src/3rdparty/utf8proc/utf8proc.c @@ -0,0 +1,773 @@ +/* -*- mode: c; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*- */ +/* + * Copyright (c) 2018 Steven G. Johnson, Jiahao Chen, Peter Colberg, Tony Kelman, Scott P. Jones, and other contributors. + * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + * + * 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. + */ + +/* + * This library contains derived data from a modified version of the + * Unicode data files. + * + * The original data files are available at + * http://www.unicode.org/Public/UNIDATA/ + * + * Please notice the copyright statement in the file "utf8proc_data.c". + */ + + +/* + * File name: utf8proc.c + * + * Description: + * Implementation of libutf8proc. + */ + + +#include "utf8proc.h" + +#ifndef SSIZE_MAX +#define SSIZE_MAX ((size_t)SIZE_MAX/2) +#endif +#ifndef UINT16_MAX +# define UINT16_MAX 65535U +#endif + +#include "utf8proc_data.c" + + +UTF8PROC_DLLEXPORT const utf8proc_int8_t utf8proc_utf8class[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 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, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#define UTF8PROC_HANGUL_SBASE 0xAC00 +#define UTF8PROC_HANGUL_LBASE 0x1100 +#define UTF8PROC_HANGUL_VBASE 0x1161 +#define UTF8PROC_HANGUL_TBASE 0x11A7 +#define UTF8PROC_HANGUL_LCOUNT 19 +#define UTF8PROC_HANGUL_VCOUNT 21 +#define UTF8PROC_HANGUL_TCOUNT 28 +#define UTF8PROC_HANGUL_NCOUNT 588 +#define UTF8PROC_HANGUL_SCOUNT 11172 +/* END is exclusive */ +#define UTF8PROC_HANGUL_L_START 0x1100 +#define UTF8PROC_HANGUL_L_END 0x115A +#define UTF8PROC_HANGUL_L_FILLER 0x115F +#define UTF8PROC_HANGUL_V_START 0x1160 +#define UTF8PROC_HANGUL_V_END 0x11A3 +#define UTF8PROC_HANGUL_T_START 0x11A8 +#define UTF8PROC_HANGUL_T_END 0x11FA +#define UTF8PROC_HANGUL_S_START 0xAC00 +#define UTF8PROC_HANGUL_S_END 0xD7A4 + +/* Should follow semantic-versioning rules (semver.org) based on API + compatibility. (Note that the shared-library version number will + be different, being based on ABI compatibility.): */ +#define STRINGIZEx(x) #x +#define STRINGIZE(x) STRINGIZEx(x) +UTF8PROC_DLLEXPORT const char *utf8proc_version(void) { + return STRINGIZE(UTF8PROC_VERSION_MAJOR) "." STRINGIZE(UTF8PROC_VERSION_MINOR) "." STRINGIZE(UTF8PROC_VERSION_PATCH) ""; +} + +UTF8PROC_DLLEXPORT const char *utf8proc_unicode_version(void) { + return "12.1.0"; +} + +UTF8PROC_DLLEXPORT const char *utf8proc_errmsg(utf8proc_ssize_t errcode) { + switch (errcode) { + case UTF8PROC_ERROR_NOMEM: + return "Memory for processing UTF-8 data could not be allocated."; + case UTF8PROC_ERROR_OVERFLOW: + return "UTF-8 string is too long to be processed."; + case UTF8PROC_ERROR_INVALIDUTF8: + return "Invalid UTF-8 string"; + case UTF8PROC_ERROR_NOTASSIGNED: + return "Unassigned Unicode code point found in UTF-8 string."; + case UTF8PROC_ERROR_INVALIDOPTS: + return "Invalid options for UTF-8 processing chosen."; + default: + return "An unknown error occurred while processing UTF-8 data."; + } +} + +#define utf_cont(ch) (((ch) & 0xc0) == 0x80) +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_int32_t *dst +) { + utf8proc_uint32_t uc; + const utf8proc_uint8_t *end; + + *dst = -1; + if (!strlen) return 0; + end = str + ((strlen < 0) ? 4 : strlen); + uc = *str++; + if (uc < 0x80) { + *dst = uc; + return 1; + } + // Must be between 0xc2 and 0xf4 inclusive to be valid + if ((uc - 0xc2) > (0xf4-0xc2)) return UTF8PROC_ERROR_INVALIDUTF8; + if (uc < 0xe0) { // 2-byte sequence + // Must have valid continuation character + if (str >= end || !utf_cont(*str)) return UTF8PROC_ERROR_INVALIDUTF8; + *dst = ((uc & 0x1f)<<6) | (*str & 0x3f); + return 2; + } + if (uc < 0xf0) { // 3-byte sequence + if ((str + 1 >= end) || !utf_cont(*str) || !utf_cont(str[1])) + return UTF8PROC_ERROR_INVALIDUTF8; + // Check for surrogate chars + if (uc == 0xed && *str > 0x9f) + return UTF8PROC_ERROR_INVALIDUTF8; + uc = ((uc & 0xf)<<12) | ((*str & 0x3f)<<6) | (str[1] & 0x3f); + if (uc < 0x800) + return UTF8PROC_ERROR_INVALIDUTF8; + *dst = uc; + return 3; + } + // 4-byte sequence + // Must have 3 valid continuation characters + if ((str + 2 >= end) || !utf_cont(*str) || !utf_cont(str[1]) || !utf_cont(str[2])) + return UTF8PROC_ERROR_INVALIDUTF8; + // Make sure in correct range (0x10000 - 0x10ffff) + if (uc == 0xf0) { + if (*str < 0x90) return UTF8PROC_ERROR_INVALIDUTF8; + } else if (uc == 0xf4) { + if (*str > 0x8f) return UTF8PROC_ERROR_INVALIDUTF8; + } + *dst = ((uc & 7)<<18) | ((*str & 0x3f)<<12) | ((str[1] & 0x3f)<<6) | (str[2] & 0x3f); + return 4; +} + +UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_codepoint_valid(utf8proc_int32_t uc) { + return (((utf8proc_uint32_t)uc)-0xd800 > 0x07ff) && ((utf8proc_uint32_t)uc < 0x110000); +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) { + if (uc < 0x00) { + return 0; + } else if (uc < 0x80) { + dst[0] = (utf8proc_uint8_t) uc; + return 1; + } else if (uc < 0x800) { + dst[0] = (utf8proc_uint8_t)(0xC0 + (uc >> 6)); + dst[1] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); + return 2; + // Note: we allow encoding 0xd800-0xdfff here, so as not to change + // the API, however, these are actually invalid in UTF-8 + } else if (uc < 0x10000) { + dst[0] = (utf8proc_uint8_t)(0xE0 + (uc >> 12)); + dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F)); + dst[2] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); + return 3; + } else if (uc < 0x110000) { + dst[0] = (utf8proc_uint8_t)(0xF0 + (uc >> 18)); + dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 12) & 0x3F)); + dst[2] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F)); + dst[3] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); + return 4; + } else return 0; +} + +/* internal version used for inserting 0xff bytes between graphemes */ +static utf8proc_ssize_t charbound_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) { + if (uc < 0x00) { + if (uc == -1) { /* internal value used for grapheme breaks */ + dst[0] = (utf8proc_uint8_t)0xFF; + return 1; + } + return 0; + } else if (uc < 0x80) { + dst[0] = (utf8proc_uint8_t)uc; + return 1; + } else if (uc < 0x800) { + dst[0] = (utf8proc_uint8_t)(0xC0 + (uc >> 6)); + dst[1] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); + return 2; + } else if (uc < 0x10000) { + dst[0] = (utf8proc_uint8_t)(0xE0 + (uc >> 12)); + dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F)); + dst[2] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); + return 3; + } else if (uc < 0x110000) { + dst[0] = (utf8proc_uint8_t)(0xF0 + (uc >> 18)); + dst[1] = (utf8proc_uint8_t)(0x80 + ((uc >> 12) & 0x3F)); + dst[2] = (utf8proc_uint8_t)(0x80 + ((uc >> 6) & 0x3F)); + dst[3] = (utf8proc_uint8_t)(0x80 + (uc & 0x3F)); + return 4; + } else return 0; +} + +/* internal "unsafe" version that does not check whether uc is in range */ +static const utf8proc_property_t *unsafe_get_property(utf8proc_int32_t uc) { + /* ASSERT: uc >= 0 && uc < 0x110000 */ + return utf8proc_properties + ( + utf8proc_stage2table[ + utf8proc_stage1table[uc >> 8] + (uc & 0xFF) + ] + ); +} + +UTF8PROC_DLLEXPORT const utf8proc_property_t *utf8proc_get_property(utf8proc_int32_t uc) { + return uc < 0 || uc >= 0x110000 ? utf8proc_properties : unsafe_get_property(uc); +} + +/* return whether there is a grapheme break between boundclasses lbc and tbc + (according to the definition of extended grapheme clusters) + + Rule numbering refers to TR29 Version 29 (Unicode 9.0.0): + http://www.unicode.org/reports/tr29/tr29-29.html + + CAVEATS: + Please note that evaluation of GB10 (grapheme breaks between emoji zwj sequences) + and GB 12/13 (regional indicator code points) require knowledge of previous characters + and are thus not handled by this function. This may result in an incorrect break before + an E_Modifier class codepoint and an incorrectly missing break between two + REGIONAL_INDICATOR class code points if such support does not exist in the caller. + + See the special support in grapheme_break_extended, for required bookkeeping by the caller. +*/ +static utf8proc_bool grapheme_break_simple(int lbc, int tbc) { + return + (lbc == UTF8PROC_BOUNDCLASS_START) ? true : // GB1 + (lbc == UTF8PROC_BOUNDCLASS_CR && // GB3 + tbc == UTF8PROC_BOUNDCLASS_LF) ? false : // --- + (lbc >= UTF8PROC_BOUNDCLASS_CR && lbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : // GB4 + (tbc >= UTF8PROC_BOUNDCLASS_CR && tbc <= UTF8PROC_BOUNDCLASS_CONTROL) ? true : // GB5 + (lbc == UTF8PROC_BOUNDCLASS_L && // GB6 + (tbc == UTF8PROC_BOUNDCLASS_L || // --- + tbc == UTF8PROC_BOUNDCLASS_V || // --- + tbc == UTF8PROC_BOUNDCLASS_LV || // --- + tbc == UTF8PROC_BOUNDCLASS_LVT)) ? false : // --- + ((lbc == UTF8PROC_BOUNDCLASS_LV || // GB7 + lbc == UTF8PROC_BOUNDCLASS_V) && // --- + (tbc == UTF8PROC_BOUNDCLASS_V || // --- + tbc == UTF8PROC_BOUNDCLASS_T)) ? false : // --- + ((lbc == UTF8PROC_BOUNDCLASS_LVT || // GB8 + lbc == UTF8PROC_BOUNDCLASS_T) && // --- + tbc == UTF8PROC_BOUNDCLASS_T) ? false : // --- + (tbc == UTF8PROC_BOUNDCLASS_EXTEND || // GB9 + tbc == UTF8PROC_BOUNDCLASS_ZWJ || // --- + tbc == UTF8PROC_BOUNDCLASS_SPACINGMARK || // GB9a + lbc == UTF8PROC_BOUNDCLASS_PREPEND) ? false : // GB9b + (lbc == UTF8PROC_BOUNDCLASS_E_ZWG && // GB11 (requires additional handling below) + tbc == UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC) ? false : // ---- + (lbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR && // GB12/13 (requires additional handling below) + tbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR) ? false : // ---- + true; // GB999 +} + +static utf8proc_bool grapheme_break_extended(int lbc, int tbc, utf8proc_int32_t *state) +{ + int lbc_override = ((state && *state != UTF8PROC_BOUNDCLASS_START) + ? *state : lbc); + utf8proc_bool break_permitted = grapheme_break_simple(lbc_override, tbc); + if (state) { + // Special support for GB 12/13 made possible by GB999. After two RI + // class codepoints we want to force a break. Do this by resetting the + // second RI's bound class to UTF8PROC_BOUNDCLASS_OTHER, to force a break + // after that character according to GB999 (unless of course such a break is + // forbidden by a different rule such as GB9). + if (*state == tbc && tbc == UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR) + *state = UTF8PROC_BOUNDCLASS_OTHER; + // Special support for GB11 (emoji extend* zwj / emoji) + else if (*state == UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC) { + if (tbc == UTF8PROC_BOUNDCLASS_EXTEND) // fold EXTEND codepoints into emoji + *state = UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC; + else if (tbc == UTF8PROC_BOUNDCLASS_ZWJ) + *state = UTF8PROC_BOUNDCLASS_E_ZWG; // state to record emoji+zwg combo + else + *state = tbc; + } + else + *state = tbc; + } + return break_permitted; +} + +UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_grapheme_break_stateful( + utf8proc_int32_t c1, utf8proc_int32_t c2, utf8proc_int32_t *state) { + + return grapheme_break_extended(utf8proc_get_property(c1)->boundclass, + utf8proc_get_property(c2)->boundclass, + state); +} + + +UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_grapheme_break( + utf8proc_int32_t c1, utf8proc_int32_t c2) { + return utf8proc_grapheme_break_stateful(c1, c2, NULL); +} + +static utf8proc_int32_t seqindex_decode_entry(const utf8proc_uint16_t **entry) +{ + utf8proc_int32_t entry_cp = **entry; + if ((entry_cp & 0xF800) == 0xD800) { + *entry = *entry + 1; + entry_cp = ((entry_cp & 0x03FF) << 10) | (**entry & 0x03FF); + entry_cp += 0x10000; + } + return entry_cp; +} + +static utf8proc_int32_t seqindex_decode_index(const utf8proc_uint32_t seqindex) +{ + const utf8proc_uint16_t *entry = &utf8proc_sequences[seqindex]; + return seqindex_decode_entry(&entry); +} + +static utf8proc_ssize_t seqindex_write_char_decomposed(utf8proc_uint16_t seqindex, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, utf8proc_option_t options, int *last_boundclass) { + utf8proc_ssize_t written = 0; + const utf8proc_uint16_t *entry = &utf8proc_sequences[seqindex & 0x1FFF]; + int len = seqindex >> 13; + if (len >= 7) { + len = *entry; + entry++; + } + for (; len >= 0; entry++, len--) { + utf8proc_int32_t entry_cp = seqindex_decode_entry(&entry); + + written += utf8proc_decompose_char(entry_cp, dst+written, + (bufsize > written) ? (bufsize - written) : 0, options, + last_boundclass); + if (written < 0) return UTF8PROC_ERROR_OVERFLOW; + } + return written; +} + +UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_tolower(utf8proc_int32_t c) +{ + utf8proc_int32_t cl = utf8proc_get_property(c)->lowercase_seqindex; + return cl != UINT16_MAX ? seqindex_decode_index(cl) : c; +} + +UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_toupper(utf8proc_int32_t c) +{ + utf8proc_int32_t cu = utf8proc_get_property(c)->uppercase_seqindex; + return cu != UINT16_MAX ? seqindex_decode_index(cu) : c; +} + +UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_totitle(utf8proc_int32_t c) +{ + utf8proc_int32_t cu = utf8proc_get_property(c)->titlecase_seqindex; + return cu != UINT16_MAX ? seqindex_decode_index(cu) : c; +} + +/* return a character width analogous to wcwidth (except portable and + hopefully less buggy than most system wcwidth functions). */ +UTF8PROC_DLLEXPORT int utf8proc_charwidth(utf8proc_int32_t c) { + return utf8proc_get_property(c)->charwidth; +} + +UTF8PROC_DLLEXPORT utf8proc_category_t utf8proc_category(utf8proc_int32_t c) { + return utf8proc_get_property(c)->category; +} + +UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t c) { + static const char s[][3] = {"Cn","Lu","Ll","Lt","Lm","Lo","Mn","Mc","Me","Nd","Nl","No","Pc","Pd","Ps","Pe","Pi","Pf","Po","Sm","Sc","Sk","So","Zs","Zl","Zp","Cc","Cf","Cs","Co"}; + return s[utf8proc_category(c)]; +} + +#define utf8proc_decompose_lump(replacement_uc) \ + return utf8proc_decompose_char((replacement_uc), dst, bufsize, \ + options & ~UTF8PROC_LUMP, last_boundclass) + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, utf8proc_option_t options, int *last_boundclass) { + const utf8proc_property_t *property; + utf8proc_propval_t category; + utf8proc_int32_t hangul_sindex; + if (uc < 0 || uc >= 0x110000) return UTF8PROC_ERROR_NOTASSIGNED; + property = unsafe_get_property(uc); + category = property->category; + hangul_sindex = uc - UTF8PROC_HANGUL_SBASE; + if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) { + if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT) { + utf8proc_int32_t hangul_tindex; + if (bufsize >= 1) { + dst[0] = UTF8PROC_HANGUL_LBASE + + hangul_sindex / UTF8PROC_HANGUL_NCOUNT; + if (bufsize >= 2) dst[1] = UTF8PROC_HANGUL_VBASE + + (hangul_sindex % UTF8PROC_HANGUL_NCOUNT) / UTF8PROC_HANGUL_TCOUNT; + } + hangul_tindex = hangul_sindex % UTF8PROC_HANGUL_TCOUNT; + if (!hangul_tindex) return 2; + if (bufsize >= 3) dst[2] = UTF8PROC_HANGUL_TBASE + hangul_tindex; + return 3; + } + } + if (options & UTF8PROC_REJECTNA) { + if (!category) return UTF8PROC_ERROR_NOTASSIGNED; + } + if (options & UTF8PROC_IGNORE) { + if (property->ignorable) return 0; + } + if (options & UTF8PROC_STRIPNA) { + if (!category) return 0; + } + if (options & UTF8PROC_LUMP) { + if (category == UTF8PROC_CATEGORY_ZS) utf8proc_decompose_lump(0x0020); + if (uc == 0x2018 || uc == 0x2019 || uc == 0x02BC || uc == 0x02C8) + utf8proc_decompose_lump(0x0027); + if (category == UTF8PROC_CATEGORY_PD || uc == 0x2212) + utf8proc_decompose_lump(0x002D); + if (uc == 0x2044 || uc == 0x2215) utf8proc_decompose_lump(0x002F); + if (uc == 0x2236) utf8proc_decompose_lump(0x003A); + if (uc == 0x2039 || uc == 0x2329 || uc == 0x3008) + utf8proc_decompose_lump(0x003C); + if (uc == 0x203A || uc == 0x232A || uc == 0x3009) + utf8proc_decompose_lump(0x003E); + if (uc == 0x2216) utf8proc_decompose_lump(0x005C); + if (uc == 0x02C4 || uc == 0x02C6 || uc == 0x2038 || uc == 0x2303) + utf8proc_decompose_lump(0x005E); + if (category == UTF8PROC_CATEGORY_PC || uc == 0x02CD) + utf8proc_decompose_lump(0x005F); + if (uc == 0x02CB) utf8proc_decompose_lump(0x0060); + if (uc == 0x2223) utf8proc_decompose_lump(0x007C); + if (uc == 0x223C) utf8proc_decompose_lump(0x007E); + if ((options & UTF8PROC_NLF2LS) && (options & UTF8PROC_NLF2PS)) { + if (category == UTF8PROC_CATEGORY_ZL || + category == UTF8PROC_CATEGORY_ZP) + utf8proc_decompose_lump(0x000A); + } + } + if (options & UTF8PROC_STRIPMARK) { + if (category == UTF8PROC_CATEGORY_MN || + category == UTF8PROC_CATEGORY_MC || + category == UTF8PROC_CATEGORY_ME) return 0; + } + if (options & UTF8PROC_CASEFOLD) { + if (property->casefold_seqindex != UINT16_MAX) { + return seqindex_write_char_decomposed(property->casefold_seqindex, dst, bufsize, options, last_boundclass); + } + } + if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) { + if (property->decomp_seqindex != UINT16_MAX && + (!property->decomp_type || (options & UTF8PROC_COMPAT))) { + return seqindex_write_char_decomposed(property->decomp_seqindex, dst, bufsize, options, last_boundclass); + } + } + if (options & UTF8PROC_CHARBOUND) { + utf8proc_bool boundary; + int tbc = property->boundclass; + boundary = grapheme_break_extended(*last_boundclass, tbc, last_boundclass); + if (boundary) { + if (bufsize >= 1) dst[0] = -1; /* sentinel value for grapheme break */ + if (bufsize >= 2) dst[1] = uc; + return 2; + } + } + if (bufsize >= 1) *dst = uc; + return 1; +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, + utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options +) { + return utf8proc_decompose_custom(str, strlen, buffer, bufsize, options, NULL, NULL); +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_custom( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, + utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options, + utf8proc_custom_func custom_func, void *custom_data +) { + /* strlen will be ignored, if UTF8PROC_NULLTERM is set in options */ + utf8proc_ssize_t wpos = 0; + if ((options & UTF8PROC_COMPOSE) && (options & UTF8PROC_DECOMPOSE)) + return UTF8PROC_ERROR_INVALIDOPTS; + if ((options & UTF8PROC_STRIPMARK) && + !(options & UTF8PROC_COMPOSE) && !(options & UTF8PROC_DECOMPOSE)) + return UTF8PROC_ERROR_INVALIDOPTS; + { + utf8proc_int32_t uc; + utf8proc_ssize_t rpos = 0; + utf8proc_ssize_t decomp_result; + int boundclass = UTF8PROC_BOUNDCLASS_START; + while (1) { + if (options & UTF8PROC_NULLTERM) { + rpos += utf8proc_iterate(str + rpos, -1, &uc); + /* checking of return value is not necessary, + as 'uc' is < 0 in case of error */ + if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8; + if (rpos < 0) return UTF8PROC_ERROR_OVERFLOW; + if (uc == 0) break; + } else { + if (rpos >= strlen) break; + rpos += utf8proc_iterate(str + rpos, strlen - rpos, &uc); + if (uc < 0) return UTF8PROC_ERROR_INVALIDUTF8; + } + if (custom_func != NULL) { + uc = custom_func(uc, custom_data); /* user-specified custom mapping */ + } + decomp_result = utf8proc_decompose_char( + uc, buffer + wpos, (bufsize > wpos) ? (bufsize - wpos) : 0, options, + &boundclass + ); + if (decomp_result < 0) return decomp_result; + wpos += decomp_result; + /* prohibiting integer overflows due to too long strings: */ + if (wpos < 0 || + wpos > (utf8proc_ssize_t)(SSIZE_MAX/sizeof(utf8proc_int32_t)/2)) + return UTF8PROC_ERROR_OVERFLOW; + } + } + if ((options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) && bufsize >= wpos) { + utf8proc_ssize_t pos = 0; + while (pos < wpos-1) { + utf8proc_int32_t uc1, uc2; + const utf8proc_property_t *property1, *property2; + uc1 = buffer[pos]; + uc2 = buffer[pos+1]; + property1 = unsafe_get_property(uc1); + property2 = unsafe_get_property(uc2); + if (property1->combining_class > property2->combining_class && + property2->combining_class > 0) { + buffer[pos] = uc2; + buffer[pos+1] = uc1; + if (pos > 0) pos--; else pos++; + } else { + pos++; + } + } + } + return wpos; +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_normalize_utf32(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options) { + /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored */ + if (options & (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS | UTF8PROC_STRIPCC)) { + utf8proc_ssize_t rpos; + utf8proc_ssize_t wpos = 0; + utf8proc_int32_t uc; + for (rpos = 0; rpos < length; rpos++) { + uc = buffer[rpos]; + if (uc == 0x000D && rpos < length-1 && buffer[rpos+1] == 0x000A) rpos++; + if (uc == 0x000A || uc == 0x000D || uc == 0x0085 || + ((options & UTF8PROC_STRIPCC) && (uc == 0x000B || uc == 0x000C))) { + if (options & UTF8PROC_NLF2LS) { + if (options & UTF8PROC_NLF2PS) { + buffer[wpos++] = 0x000A; + } else { + buffer[wpos++] = 0x2028; + } + } else { + if (options & UTF8PROC_NLF2PS) { + buffer[wpos++] = 0x2029; + } else { + buffer[wpos++] = 0x0020; + } + } + } else if ((options & UTF8PROC_STRIPCC) && + (uc < 0x0020 || (uc >= 0x007F && uc < 0x00A0))) { + if (uc == 0x0009) buffer[wpos++] = 0x0020; + } else { + buffer[wpos++] = uc; + } + } + length = wpos; + } + if (options & UTF8PROC_COMPOSE) { + utf8proc_int32_t *starter = NULL; + utf8proc_int32_t current_char; + const utf8proc_property_t *starter_property = NULL, *current_property; + utf8proc_propval_t max_combining_class = -1; + utf8proc_ssize_t rpos; + utf8proc_ssize_t wpos = 0; + utf8proc_int32_t composition; + for (rpos = 0; rpos < length; rpos++) { + current_char = buffer[rpos]; + current_property = unsafe_get_property(current_char); + if (starter && current_property->combining_class > max_combining_class) { + /* combination perhaps possible */ + utf8proc_int32_t hangul_lindex; + utf8proc_int32_t hangul_sindex; + hangul_lindex = *starter - UTF8PROC_HANGUL_LBASE; + if (hangul_lindex >= 0 && hangul_lindex < UTF8PROC_HANGUL_LCOUNT) { + utf8proc_int32_t hangul_vindex; + hangul_vindex = current_char - UTF8PROC_HANGUL_VBASE; + if (hangul_vindex >= 0 && hangul_vindex < UTF8PROC_HANGUL_VCOUNT) { + *starter = UTF8PROC_HANGUL_SBASE + + (hangul_lindex * UTF8PROC_HANGUL_VCOUNT + hangul_vindex) * + UTF8PROC_HANGUL_TCOUNT; + starter_property = NULL; + continue; + } + } + hangul_sindex = *starter - UTF8PROC_HANGUL_SBASE; + if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT && + (hangul_sindex % UTF8PROC_HANGUL_TCOUNT) == 0) { + utf8proc_int32_t hangul_tindex; + hangul_tindex = current_char - UTF8PROC_HANGUL_TBASE; + if (hangul_tindex >= 0 && hangul_tindex < UTF8PROC_HANGUL_TCOUNT) { + *starter += hangul_tindex; + starter_property = NULL; + continue; + } + } + if (!starter_property) { + starter_property = unsafe_get_property(*starter); + } + if (starter_property->comb_index < 0x8000 && + current_property->comb_index != UINT16_MAX && + current_property->comb_index >= 0x8000) { + int sidx = starter_property->comb_index; + int idx = current_property->comb_index & 0x3FFF; + if (idx >= utf8proc_combinations[sidx] && idx <= utf8proc_combinations[sidx + 1] ) { + idx += sidx + 2 - utf8proc_combinations[sidx]; + if (current_property->comb_index & 0x4000) { + composition = (utf8proc_combinations[idx] << 16) | utf8proc_combinations[idx+1]; + } else + composition = utf8proc_combinations[idx]; + + if (composition > 0 && (!(options & UTF8PROC_STABLE) || + !(unsafe_get_property(composition)->comp_exclusion))) { + *starter = composition; + starter_property = NULL; + continue; + } + } + } + } + buffer[wpos] = current_char; + if (current_property->combining_class) { + if (current_property->combining_class > max_combining_class) { + max_combining_class = current_property->combining_class; + } + } else { + starter = buffer + wpos; + starter_property = NULL; + max_combining_class = -1; + } + wpos++; + } + length = wpos; + } + return length; +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options) { + /* UTF8PROC_NULLTERM option will be ignored, 'length' is never ignored + ASSERT: 'buffer' has one spare byte of free space at the end! */ + length = utf8proc_normalize_utf32(buffer, length, options); + if (length < 0) return length; + { + utf8proc_ssize_t rpos, wpos = 0; + utf8proc_int32_t uc; + if (options & UTF8PROC_CHARBOUND) { + for (rpos = 0; rpos < length; rpos++) { + uc = buffer[rpos]; + wpos += charbound_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos); + } + } else { + for (rpos = 0; rpos < length; rpos++) { + uc = buffer[rpos]; + wpos += utf8proc_encode_char(uc, ((utf8proc_uint8_t *)buffer) + wpos); + } + } + ((utf8proc_uint8_t *)buffer)[wpos] = 0; + return wpos; + } +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options +) { + return utf8proc_map_custom(str, strlen, dstptr, options, NULL, NULL); +} + +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map_custom( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options, + utf8proc_custom_func custom_func, void *custom_data +) { + utf8proc_int32_t *buffer; + utf8proc_ssize_t result; + *dstptr = NULL; + result = utf8proc_decompose_custom(str, strlen, NULL, 0, options, custom_func, custom_data); + if (result < 0) return result; + buffer = (utf8proc_int32_t *) malloc(result * sizeof(utf8proc_int32_t) + 1); + if (!buffer) return UTF8PROC_ERROR_NOMEM; + result = utf8proc_decompose_custom(str, strlen, buffer, result, options, custom_func, custom_data); + if (result < 0) { + free(buffer); + return result; + } + result = utf8proc_reencode(buffer, result, options); + if (result < 0) { + free(buffer); + return result; + } + { + utf8proc_int32_t *newptr; + newptr = (utf8proc_int32_t *) realloc(buffer, (size_t)result+1); + if (newptr) buffer = newptr; + } + *dstptr = (utf8proc_uint8_t *)buffer; + return result; +} + +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str) { + utf8proc_uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_DECOMPOSE); + return retval; +} + +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str) { + utf8proc_uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_COMPOSE); + return retval; +} + +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str) { + utf8proc_uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT); + return retval; +} + +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str) { + utf8proc_uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_COMPOSE | UTF8PROC_COMPAT); + return retval; +} + +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC_Casefold(const utf8proc_uint8_t *str) { + utf8proc_uint8_t *retval; + utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE | + UTF8PROC_COMPOSE | UTF8PROC_COMPAT | UTF8PROC_CASEFOLD | UTF8PROC_IGNORE); + return retval; +} diff --git a/hilti/src/3rdparty/utf8proc/utf8proc.h b/hilti/src/3rdparty/utf8proc/utf8proc.h new file mode 100644 index 000000000..b25e063b4 --- /dev/null +++ b/hilti/src/3rdparty/utf8proc/utf8proc.h @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2018 Steven G. Johnson, Jiahao Chen, Peter Colberg, Tony Kelman, Scott P. Jones, and other contributors. + * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + * + * 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. + */ + + +/** + * @mainpage + * + * utf8proc is a free/open-source (MIT/expat licensed) C library + * providing Unicode normalization, case-folding, and other operations + * for strings in the UTF-8 encoding, supporting Unicode version + * 9.0.0. See the utf8proc home page (http://julialang.org/utf8proc/) + * for downloads and other information, or the source code on github + * (https://github.com/JuliaLang/utf8proc). + * + * For the utf8proc API documentation, see: @ref utf8proc.h + * + * The features of utf8proc include: + * + * - Transformation of strings (@ref utf8proc_map) to: + * - decompose (@ref UTF8PROC_DECOMPOSE) or compose (@ref UTF8PROC_COMPOSE) Unicode combining characters (http://en.wikipedia.org/wiki/Combining_character) + * - canonicalize Unicode compatibility characters (@ref UTF8PROC_COMPAT) + * - strip "ignorable" (@ref UTF8PROC_IGNORE) characters, control characters (@ref UTF8PROC_STRIPCC), or combining characters such as accents (@ref UTF8PROC_STRIPMARK) + * - case-folding (@ref UTF8PROC_CASEFOLD) + * - Unicode normalization: @ref utf8proc_NFD, @ref utf8proc_NFC, @ref utf8proc_NFKD, @ref utf8proc_NFKC + * - Detecting grapheme boundaries (@ref utf8proc_grapheme_break and @ref UTF8PROC_CHARBOUND) + * - Character-width computation: @ref utf8proc_charwidth + * - Classification of characters by Unicode category: @ref utf8proc_category and @ref utf8proc_category_string + * - Encode (@ref utf8proc_encode_char) and decode (@ref utf8proc_iterate) Unicode codepoints to/from UTF-8. + */ + +/** @file */ + +#ifndef UTF8PROC_H +#define UTF8PROC_H + +/** @name API version + * + * The utf8proc API version MAJOR.MINOR.PATCH, following + * semantic-versioning rules (http://semver.org) based on API + * compatibility. + * + * This is also returned at runtime by @ref utf8proc_version; however, the + * runtime version may append a string like "-dev" to the version number + * for prerelease versions. + * + * @note The shared-library version number in the Makefile + * (and CMakeLists.txt, and MANIFEST) may be different, + * being based on ABI compatibility rather than API compatibility. + */ +/** @{ */ +/** The MAJOR version number (increased when backwards API compatibility is broken). */ +#define UTF8PROC_VERSION_MAJOR 2 +/** The MINOR version number (increased when new functionality is added in a backwards-compatible manner). */ +#define UTF8PROC_VERSION_MINOR 4 +/** The PATCH version (increased for fixes that do not change the API). */ +#define UTF8PROC_VERSION_PATCH 0 +/** @} */ + +#include + +#if defined(_MSC_VER) && _MSC_VER < 1800 +// MSVC prior to 2013 lacked stdbool.h and inttypes.h +typedef signed char utf8proc_int8_t; +typedef unsigned char utf8proc_uint8_t; +typedef short utf8proc_int16_t; +typedef unsigned short utf8proc_uint16_t; +typedef int utf8proc_int32_t; +typedef unsigned int utf8proc_uint32_t; +# ifdef _WIN64 +typedef __int64 utf8proc_ssize_t; +typedef unsigned __int64 utf8proc_size_t; +# else +typedef int utf8proc_ssize_t; +typedef unsigned int utf8proc_size_t; +# endif +# ifndef __cplusplus +// emulate C99 bool +typedef unsigned char utf8proc_bool; +# ifndef __bool_true_false_are_defined +# define false 0 +# define true 1 +# define __bool_true_false_are_defined 1 +# endif +# else +typedef bool utf8proc_bool; +# endif +#else +# include +# include +# include +typedef int8_t utf8proc_int8_t; +typedef uint8_t utf8proc_uint8_t; +typedef int16_t utf8proc_int16_t; +typedef uint16_t utf8proc_uint16_t; +typedef int32_t utf8proc_int32_t; +typedef uint32_t utf8proc_uint32_t; +typedef size_t utf8proc_size_t; +typedef ptrdiff_t utf8proc_ssize_t; +typedef bool utf8proc_bool; +#endif +#include + +#ifdef UTF8PROC_STATIC +# define UTF8PROC_DLLEXPORT +#else +# ifdef _WIN32 +# ifdef UTF8PROC_EXPORTS +# define UTF8PROC_DLLEXPORT __declspec(dllexport) +# else +# define UTF8PROC_DLLEXPORT __declspec(dllimport) +# endif +# elif __GNUC__ >= 4 +# define UTF8PROC_DLLEXPORT __attribute__ ((visibility("default"))) +# else +# define UTF8PROC_DLLEXPORT +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Option flags used by several functions in the library. + */ +typedef enum { + /** The given UTF-8 input is NULL terminated. */ + UTF8PROC_NULLTERM = (1<<0), + /** Unicode Versioning Stability has to be respected. */ + UTF8PROC_STABLE = (1<<1), + /** Compatibility decomposition (i.e. formatting information is lost). */ + UTF8PROC_COMPAT = (1<<2), + /** Return a result with decomposed characters. */ + UTF8PROC_COMPOSE = (1<<3), + /** Return a result with decomposed characters. */ + UTF8PROC_DECOMPOSE = (1<<4), + /** Strip "default ignorable characters" such as SOFT-HYPHEN or ZERO-WIDTH-SPACE. */ + UTF8PROC_IGNORE = (1<<5), + /** Return an error, if the input contains unassigned codepoints. */ + UTF8PROC_REJECTNA = (1<<6), + /** + * Indicating that NLF-sequences (LF, CRLF, CR, NEL) are representing a + * line break, and should be converted to the codepoint for line + * separation (LS). + */ + UTF8PROC_NLF2LS = (1<<7), + /** + * Indicating that NLF-sequences are representing a paragraph break, and + * should be converted to the codepoint for paragraph separation + * (PS). + */ + UTF8PROC_NLF2PS = (1<<8), + /** Indicating that the meaning of NLF-sequences is unknown. */ + UTF8PROC_NLF2LF = (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS), + /** Strips and/or convers control characters. + * + * NLF-sequences are transformed into space, except if one of the + * NLF2LS/PS/LF options is given. HorizontalTab (HT) and FormFeed (FF) + * are treated as a NLF-sequence in this case. All other control + * characters are simply removed. + */ + UTF8PROC_STRIPCC = (1<<9), + /** + * Performs unicode case folding, to be able to do a case-insensitive + * string comparison. + */ + UTF8PROC_CASEFOLD = (1<<10), + /** + * Inserts 0xFF bytes at the beginning of each sequence which is + * representing a single grapheme cluster (see UAX#29). + */ + UTF8PROC_CHARBOUND = (1<<11), + /** Lumps certain characters together. + * + * E.g. HYPHEN U+2010 and MINUS U+2212 to ASCII "-". See lump.md for details. + * + * If NLF2LF is set, this includes a transformation of paragraph and + * line separators to ASCII line-feed (LF). + */ + UTF8PROC_LUMP = (1<<12), + /** Strips all character markings. + * + * This includes non-spacing, spacing and enclosing (i.e. accents). + * @note This option works only with @ref UTF8PROC_COMPOSE or + * @ref UTF8PROC_DECOMPOSE + */ + UTF8PROC_STRIPMARK = (1<<13), + /** + * Strip unassigned codepoints. + */ + UTF8PROC_STRIPNA = (1<<14), +} utf8proc_option_t; + +/** @name Error codes + * Error codes being returned by almost all functions. + */ +/** @{ */ +/** Memory could not be allocated. */ +#define UTF8PROC_ERROR_NOMEM -1 +/** The given string is too long to be processed. */ +#define UTF8PROC_ERROR_OVERFLOW -2 +/** The given string is not a legal UTF-8 string. */ +#define UTF8PROC_ERROR_INVALIDUTF8 -3 +/** The @ref UTF8PROC_REJECTNA flag was set and an unassigned codepoint was found. */ +#define UTF8PROC_ERROR_NOTASSIGNED -4 +/** Invalid options have been used. */ +#define UTF8PROC_ERROR_INVALIDOPTS -5 +/** @} */ + +/* @name Types */ + +/** Holds the value of a property. */ +typedef utf8proc_int16_t utf8proc_propval_t; + +/** Struct containing information about a codepoint. */ +typedef struct utf8proc_property_struct { + /** + * Unicode category. + * @see utf8proc_category_t. + */ + utf8proc_propval_t category; + utf8proc_propval_t combining_class; + /** + * Bidirectional class. + * @see utf8proc_bidi_class_t. + */ + utf8proc_propval_t bidi_class; + /** + * @anchor Decomposition type. + * @see utf8proc_decomp_type_t. + */ + utf8proc_propval_t decomp_type; + utf8proc_uint16_t decomp_seqindex; + utf8proc_uint16_t casefold_seqindex; + utf8proc_uint16_t uppercase_seqindex; + utf8proc_uint16_t lowercase_seqindex; + utf8proc_uint16_t titlecase_seqindex; + utf8proc_uint16_t comb_index; + unsigned bidi_mirrored:1; + unsigned comp_exclusion:1; + /** + * Can this codepoint be ignored? + * + * Used by @ref utf8proc_decompose_char when @ref UTF8PROC_IGNORE is + * passed as an option. + */ + unsigned ignorable:1; + unsigned control_boundary:1; + /** The width of the codepoint. */ + unsigned charwidth:2; + unsigned pad:2; + /** + * Boundclass. + * @see utf8proc_boundclass_t. + */ + unsigned boundclass:8; +} utf8proc_property_t; + +/** Unicode categories. */ +typedef enum { + UTF8PROC_CATEGORY_CN = 0, /**< Other, not assigned */ + UTF8PROC_CATEGORY_LU = 1, /**< Letter, uppercase */ + UTF8PROC_CATEGORY_LL = 2, /**< Letter, lowercase */ + UTF8PROC_CATEGORY_LT = 3, /**< Letter, titlecase */ + UTF8PROC_CATEGORY_LM = 4, /**< Letter, modifier */ + UTF8PROC_CATEGORY_LO = 5, /**< Letter, other */ + UTF8PROC_CATEGORY_MN = 6, /**< Mark, nonspacing */ + UTF8PROC_CATEGORY_MC = 7, /**< Mark, spacing combining */ + UTF8PROC_CATEGORY_ME = 8, /**< Mark, enclosing */ + UTF8PROC_CATEGORY_ND = 9, /**< Number, decimal digit */ + UTF8PROC_CATEGORY_NL = 10, /**< Number, letter */ + UTF8PROC_CATEGORY_NO = 11, /**< Number, other */ + UTF8PROC_CATEGORY_PC = 12, /**< Punctuation, connector */ + UTF8PROC_CATEGORY_PD = 13, /**< Punctuation, dash */ + UTF8PROC_CATEGORY_PS = 14, /**< Punctuation, open */ + UTF8PROC_CATEGORY_PE = 15, /**< Punctuation, close */ + UTF8PROC_CATEGORY_PI = 16, /**< Punctuation, initial quote */ + UTF8PROC_CATEGORY_PF = 17, /**< Punctuation, final quote */ + UTF8PROC_CATEGORY_PO = 18, /**< Punctuation, other */ + UTF8PROC_CATEGORY_SM = 19, /**< Symbol, math */ + UTF8PROC_CATEGORY_SC = 20, /**< Symbol, currency */ + UTF8PROC_CATEGORY_SK = 21, /**< Symbol, modifier */ + UTF8PROC_CATEGORY_SO = 22, /**< Symbol, other */ + UTF8PROC_CATEGORY_ZS = 23, /**< Separator, space */ + UTF8PROC_CATEGORY_ZL = 24, /**< Separator, line */ + UTF8PROC_CATEGORY_ZP = 25, /**< Separator, paragraph */ + UTF8PROC_CATEGORY_CC = 26, /**< Other, control */ + UTF8PROC_CATEGORY_CF = 27, /**< Other, format */ + UTF8PROC_CATEGORY_CS = 28, /**< Other, surrogate */ + UTF8PROC_CATEGORY_CO = 29, /**< Other, private use */ +} utf8proc_category_t; + +/** Bidirectional character classes. */ +typedef enum { + UTF8PROC_BIDI_CLASS_L = 1, /**< Left-to-Right */ + UTF8PROC_BIDI_CLASS_LRE = 2, /**< Left-to-Right Embedding */ + UTF8PROC_BIDI_CLASS_LRO = 3, /**< Left-to-Right Override */ + UTF8PROC_BIDI_CLASS_R = 4, /**< Right-to-Left */ + UTF8PROC_BIDI_CLASS_AL = 5, /**< Right-to-Left Arabic */ + UTF8PROC_BIDI_CLASS_RLE = 6, /**< Right-to-Left Embedding */ + UTF8PROC_BIDI_CLASS_RLO = 7, /**< Right-to-Left Override */ + UTF8PROC_BIDI_CLASS_PDF = 8, /**< Pop Directional Format */ + UTF8PROC_BIDI_CLASS_EN = 9, /**< European Number */ + UTF8PROC_BIDI_CLASS_ES = 10, /**< European Separator */ + UTF8PROC_BIDI_CLASS_ET = 11, /**< European Number Terminator */ + UTF8PROC_BIDI_CLASS_AN = 12, /**< Arabic Number */ + UTF8PROC_BIDI_CLASS_CS = 13, /**< Common Number Separator */ + UTF8PROC_BIDI_CLASS_NSM = 14, /**< Nonspacing Mark */ + UTF8PROC_BIDI_CLASS_BN = 15, /**< Boundary Neutral */ + UTF8PROC_BIDI_CLASS_B = 16, /**< Paragraph Separator */ + UTF8PROC_BIDI_CLASS_S = 17, /**< Segment Separator */ + UTF8PROC_BIDI_CLASS_WS = 18, /**< Whitespace */ + UTF8PROC_BIDI_CLASS_ON = 19, /**< Other Neutrals */ + UTF8PROC_BIDI_CLASS_LRI = 20, /**< Left-to-Right Isolate */ + UTF8PROC_BIDI_CLASS_RLI = 21, /**< Right-to-Left Isolate */ + UTF8PROC_BIDI_CLASS_FSI = 22, /**< First Strong Isolate */ + UTF8PROC_BIDI_CLASS_PDI = 23, /**< Pop Directional Isolate */ +} utf8proc_bidi_class_t; + +/** Decomposition type. */ +typedef enum { + UTF8PROC_DECOMP_TYPE_FONT = 1, /**< Font */ + UTF8PROC_DECOMP_TYPE_NOBREAK = 2, /**< Nobreak */ + UTF8PROC_DECOMP_TYPE_INITIAL = 3, /**< Initial */ + UTF8PROC_DECOMP_TYPE_MEDIAL = 4, /**< Medial */ + UTF8PROC_DECOMP_TYPE_FINAL = 5, /**< Final */ + UTF8PROC_DECOMP_TYPE_ISOLATED = 6, /**< Isolated */ + UTF8PROC_DECOMP_TYPE_CIRCLE = 7, /**< Circle */ + UTF8PROC_DECOMP_TYPE_SUPER = 8, /**< Super */ + UTF8PROC_DECOMP_TYPE_SUB = 9, /**< Sub */ + UTF8PROC_DECOMP_TYPE_VERTICAL = 10, /**< Vertical */ + UTF8PROC_DECOMP_TYPE_WIDE = 11, /**< Wide */ + UTF8PROC_DECOMP_TYPE_NARROW = 12, /**< Narrow */ + UTF8PROC_DECOMP_TYPE_SMALL = 13, /**< Small */ + UTF8PROC_DECOMP_TYPE_SQUARE = 14, /**< Square */ + UTF8PROC_DECOMP_TYPE_FRACTION = 15, /**< Fraction */ + UTF8PROC_DECOMP_TYPE_COMPAT = 16, /**< Compat */ +} utf8proc_decomp_type_t; + +/** Boundclass property. (TR29) */ +typedef enum { + UTF8PROC_BOUNDCLASS_START = 0, /**< Start */ + UTF8PROC_BOUNDCLASS_OTHER = 1, /**< Other */ + UTF8PROC_BOUNDCLASS_CR = 2, /**< Cr */ + UTF8PROC_BOUNDCLASS_LF = 3, /**< Lf */ + UTF8PROC_BOUNDCLASS_CONTROL = 4, /**< Control */ + UTF8PROC_BOUNDCLASS_EXTEND = 5, /**< Extend */ + UTF8PROC_BOUNDCLASS_L = 6, /**< L */ + UTF8PROC_BOUNDCLASS_V = 7, /**< V */ + UTF8PROC_BOUNDCLASS_T = 8, /**< T */ + UTF8PROC_BOUNDCLASS_LV = 9, /**< Lv */ + UTF8PROC_BOUNDCLASS_LVT = 10, /**< Lvt */ + UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR = 11, /**< Regional indicator */ + UTF8PROC_BOUNDCLASS_SPACINGMARK = 12, /**< Spacingmark */ + UTF8PROC_BOUNDCLASS_PREPEND = 13, /**< Prepend */ + UTF8PROC_BOUNDCLASS_ZWJ = 14, /**< Zero Width Joiner */ + + /* the following are no longer used in Unicode 11, but we keep + the constants here for backward compatibility */ + UTF8PROC_BOUNDCLASS_E_BASE = 15, /**< Emoji Base */ + UTF8PROC_BOUNDCLASS_E_MODIFIER = 16, /**< Emoji Modifier */ + UTF8PROC_BOUNDCLASS_GLUE_AFTER_ZWJ = 17, /**< Glue_After_ZWJ */ + UTF8PROC_BOUNDCLASS_E_BASE_GAZ = 18, /**< E_BASE + GLUE_AFTER_ZJW */ + + /* the Extended_Pictographic property is used in the Unicode 11 + grapheme-boundary rules, so we store it in the boundclass field */ + UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC = 19, + UTF8PROC_BOUNDCLASS_E_ZWG = 20, /* UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC + ZWJ */ +} utf8proc_boundclass_t; + +/** + * Function pointer type passed to @ref utf8proc_map_custom and + * @ref utf8proc_decompose_custom, which is used to specify a user-defined + * mapping of codepoints to be applied in conjunction with other mappings. + */ +typedef utf8proc_int32_t (*utf8proc_custom_func)(utf8proc_int32_t codepoint, void *data); + +/** + * Array containing the byte lengths of a UTF-8 encoded codepoint based + * on the first byte. + */ +UTF8PROC_DLLEXPORT extern const utf8proc_int8_t utf8proc_utf8class[256]; + +/** + * Returns the utf8proc API version as a string MAJOR.MINOR.PATCH + * (http://semver.org format), possibly with a "-dev" suffix for + * development versions. + */ +UTF8PROC_DLLEXPORT const char *utf8proc_version(void); + +/** + * Returns the utf8proc supported Unicode version as a string MAJOR.MINOR.PATCH. + */ +UTF8PROC_DLLEXPORT const char *utf8proc_unicode_version(void); + +/** + * Returns an informative error string for the given utf8proc error code + * (e.g. the error codes returned by @ref utf8proc_map). + */ +UTF8PROC_DLLEXPORT const char *utf8proc_errmsg(utf8proc_ssize_t errcode); + +/** + * Reads a single codepoint from the UTF-8 sequence being pointed to by `str`. + * The maximum number of bytes read is `strlen`, unless `strlen` is + * negative (in which case up to 4 bytes are read). + * + * If a valid codepoint could be read, it is stored in the variable + * pointed to by `codepoint_ref`, otherwise that variable will be set to -1. + * In case of success, the number of bytes read is returned; otherwise, a + * negative error code is returned. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_iterate(const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_int32_t *codepoint_ref); + +/** + * Check if a codepoint is valid (regardless of whether it has been + * assigned a value by the current Unicode standard). + * + * @return 1 if the given `codepoint` is valid and otherwise return 0. + */ +UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_codepoint_valid(utf8proc_int32_t codepoint); + +/** + * Encodes the codepoint as an UTF-8 string in the byte array pointed + * to by `dst`. This array must be at least 4 bytes long. + * + * In case of success the number of bytes written is returned, and + * otherwise 0 is returned. + * + * This function does not check whether `codepoint` is valid Unicode. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t codepoint, utf8proc_uint8_t *dst); + +/** + * Look up the properties for a given codepoint. + * + * @param codepoint The Unicode codepoint. + * + * @returns + * A pointer to a (constant) struct containing information about + * the codepoint. + * @par + * If the codepoint is unassigned or invalid, a pointer to a special struct is + * returned in which `category` is 0 (@ref UTF8PROC_CATEGORY_CN). + */ +UTF8PROC_DLLEXPORT const utf8proc_property_t *utf8proc_get_property(utf8proc_int32_t codepoint); + +/** Decompose a codepoint into an array of codepoints. + * + * @param codepoint the codepoint. + * @param dst the destination buffer. + * @param bufsize the size of the destination buffer. + * @param options one or more of the following flags: + * - @ref UTF8PROC_REJECTNA - return an error `codepoint` is unassigned + * - @ref UTF8PROC_IGNORE - strip "default ignorable" codepoints + * - @ref UTF8PROC_CASEFOLD - apply Unicode casefolding + * - @ref UTF8PROC_COMPAT - replace certain codepoints with their + * compatibility decomposition + * - @ref UTF8PROC_CHARBOUND - insert 0xFF bytes before each grapheme cluster + * - @ref UTF8PROC_LUMP - lump certain different codepoints together + * - @ref UTF8PROC_STRIPMARK - remove all character marks + * - @ref UTF8PROC_STRIPNA - remove unassigned codepoints + * @param last_boundclass + * Pointer to an integer variable containing + * the previous codepoint's boundary class if the @ref UTF8PROC_CHARBOUND + * option is used. Otherwise, this parameter is ignored. + * + * @return + * In case of success, the number of codepoints written is returned; in case + * of an error, a negative error code is returned (@ref utf8proc_errmsg). + * @par + * If the number of written codepoints would be bigger than `bufsize`, the + * required buffer size is returned, while the buffer will be overwritten with + * undefined data. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char( + utf8proc_int32_t codepoint, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, + utf8proc_option_t options, int *last_boundclass +); + +/** + * The same as @ref utf8proc_decompose_char, but acts on a whole UTF-8 + * string and orders the decomposed sequences correctly. + * + * If the @ref UTF8PROC_NULLTERM flag in `options` is set, processing + * will be stopped, when a NULL byte is encounted, otherwise `strlen` + * bytes are processed. The result (in the form of 32-bit unicode + * codepoints) is written into the buffer being pointed to by + * `buffer` (which must contain at least `bufsize` entries). In case of + * success, the number of codepoints written is returned; in case of an + * error, a negative error code is returned (@ref utf8proc_errmsg). + * See @ref utf8proc_decompose_custom to supply additional transformations. + * + * If the number of written codepoints would be bigger than `bufsize`, the + * required buffer size is returned, while the buffer will be overwritten with + * undefined data. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, + utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options +); + +/** + * The same as @ref utf8proc_decompose, but also takes a `custom_func` mapping function + * that is called on each codepoint in `str` before any other transformations + * (along with a `custom_data` pointer that is passed through to `custom_func`). + * The `custom_func` argument is ignored if it is `NULL`. See also @ref utf8proc_map_custom. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_custom( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, + utf8proc_int32_t *buffer, utf8proc_ssize_t bufsize, utf8proc_option_t options, + utf8proc_custom_func custom_func, void *custom_data +); + +/** + * Normalizes the sequence of `length` codepoints pointed to by `buffer` + * in-place (i.e., the result is also stored in `buffer`). + * + * @param buffer the (native-endian UTF-32) unicode codepoints to re-encode. + * @param length the length (in codepoints) of the buffer. + * @param options a bitwise or (`|`) of one or more of the following flags: + * - @ref UTF8PROC_NLF2LS - convert LF, CRLF, CR and NEL into LS + * - @ref UTF8PROC_NLF2PS - convert LF, CRLF, CR and NEL into PS + * - @ref UTF8PROC_NLF2LF - convert LF, CRLF, CR and NEL into LF + * - @ref UTF8PROC_STRIPCC - strip or convert all non-affected control characters + * - @ref UTF8PROC_COMPOSE - try to combine decomposed codepoints into composite + * codepoints + * - @ref UTF8PROC_STABLE - prohibit combining characters that would violate + * the unicode versioning stability + * + * @return + * In case of success, the length (in codepoints) of the normalized UTF-32 string is + * returned; otherwise, a negative error code is returned (@ref utf8proc_errmsg). + * + * @warning The entries of the array pointed to by `str` have to be in the + * range `0x0000` to `0x10FFFF`. Otherwise, the program might crash! + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_normalize_utf32(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options); + +/** + * Reencodes the sequence of `length` codepoints pointed to by `buffer` + * UTF-8 data in-place (i.e., the result is also stored in `buffer`). + * Can optionally normalize the UTF-32 sequence prior to UTF-8 conversion. + * + * @param buffer the (native-endian UTF-32) unicode codepoints to re-encode. + * @param length the length (in codepoints) of the buffer. + * @param options a bitwise or (`|`) of one or more of the following flags: + * - @ref UTF8PROC_NLF2LS - convert LF, CRLF, CR and NEL into LS + * - @ref UTF8PROC_NLF2PS - convert LF, CRLF, CR and NEL into PS + * - @ref UTF8PROC_NLF2LF - convert LF, CRLF, CR and NEL into LF + * - @ref UTF8PROC_STRIPCC - strip or convert all non-affected control characters + * - @ref UTF8PROC_COMPOSE - try to combine decomposed codepoints into composite + * codepoints + * - @ref UTF8PROC_STABLE - prohibit combining characters that would violate + * the unicode versioning stability + * - @ref UTF8PROC_CHARBOUND - insert 0xFF bytes before each grapheme cluster + * + * @return + * In case of success, the length (in bytes) of the resulting nul-terminated + * UTF-8 string is returned; otherwise, a negative error code is returned + * (@ref utf8proc_errmsg). + * + * @warning The amount of free space pointed to by `buffer` must + * exceed the amount of the input data by one byte, and the + * entries of the array pointed to by `str` have to be in the + * range `0x0000` to `0x10FFFF`. Otherwise, the program might crash! + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_reencode(utf8proc_int32_t *buffer, utf8proc_ssize_t length, utf8proc_option_t options); + +/** + * Given a pair of consecutive codepoints, return whether a grapheme break is + * permitted between them (as defined by the extended grapheme clusters in UAX#29). + * + * @param codepoint1 The first codepoint. + * @param codepoint2 The second codepoint, occurring consecutively after `codepoint1`. + * @param state Beginning with Version 29 (Unicode 9.0.0), this algorithm requires + * state to break graphemes. This state can be passed in as a pointer + * in the `state` argument and should initially be set to 0. If the + * state is not passed in (i.e. a null pointer is passed), UAX#29 rules + * GB10/12/13 which require this state will not be applied, essentially + * matching the rules in Unicode 8.0.0. + * + * @warning If the state parameter is used, `utf8proc_grapheme_break_stateful` must + * be called IN ORDER on ALL potential breaks in a string. However, it + * is safe to reset the state to zero after a grapheme break. + */ +UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_grapheme_break_stateful( + utf8proc_int32_t codepoint1, utf8proc_int32_t codepoint2, utf8proc_int32_t *state); + +/** + * Same as @ref utf8proc_grapheme_break_stateful, except without support for the + * Unicode 9 additions to the algorithm. Supported for legacy reasons. + */ +UTF8PROC_DLLEXPORT utf8proc_bool utf8proc_grapheme_break( + utf8proc_int32_t codepoint1, utf8proc_int32_t codepoint2); + + +/** + * Given a codepoint `c`, return the codepoint of the corresponding + * lower-case character, if any; otherwise (if there is no lower-case + * variant, or if `c` is not a valid codepoint) return `c`. + */ +UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_tolower(utf8proc_int32_t c); + +/** + * Given a codepoint `c`, return the codepoint of the corresponding + * upper-case character, if any; otherwise (if there is no upper-case + * variant, or if `c` is not a valid codepoint) return `c`. + */ +UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_toupper(utf8proc_int32_t c); + +/** + * Given a codepoint `c`, return the codepoint of the corresponding + * title-case character, if any; otherwise (if there is no title-case + * variant, or if `c` is not a valid codepoint) return `c`. + */ +UTF8PROC_DLLEXPORT utf8proc_int32_t utf8proc_totitle(utf8proc_int32_t c); + +/** + * Given a codepoint, return a character width analogous to `wcwidth(codepoint)`, + * except that a width of 0 is returned for non-printable codepoints + * instead of -1 as in `wcwidth`. + * + * @note + * If you want to check for particular types of non-printable characters, + * (analogous to `isprint` or `iscntrl`), use @ref utf8proc_category. */ +UTF8PROC_DLLEXPORT int utf8proc_charwidth(utf8proc_int32_t codepoint); + +/** + * Return the Unicode category for the codepoint (one of the + * @ref utf8proc_category_t constants.) + */ +UTF8PROC_DLLEXPORT utf8proc_category_t utf8proc_category(utf8proc_int32_t codepoint); + +/** + * Return the two-letter (nul-terminated) Unicode category string for + * the codepoint (e.g. `"Lu"` or `"Co"`). + */ +UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t codepoint); + +/** + * Maps the given UTF-8 string pointed to by `str` to a new UTF-8 + * string, allocated dynamically by `malloc` and returned via `dstptr`. + * + * If the @ref UTF8PROC_NULLTERM flag in the `options` field is set, + * the length is determined by a NULL terminator, otherwise the + * parameter `strlen` is evaluated to determine the string length, but + * in any case the result will be NULL terminated (though it might + * contain NULL characters with the string if `str` contained NULL + * characters). Other flags in the `options` field are passed to the + * functions defined above, and regarded as described. See also + * @ref utf8proc_map_custom to supply a custom codepoint transformation. + * + * In case of success the length of the new string is returned, + * otherwise a negative error code is returned. + * + * @note The memory of the new UTF-8 string will have been allocated + * with `malloc`, and should therefore be deallocated with `free`. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options +); + +/** + * Like @ref utf8proc_map, but also takes a `custom_func` mapping function + * that is called on each codepoint in `str` before any other transformations + * (along with a `custom_data` pointer that is passed through to `custom_func`). + * The `custom_func` argument is ignored if it is `NULL`. + */ +UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map_custom( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_uint8_t **dstptr, utf8proc_option_t options, + utf8proc_custom_func custom_func, void *custom_data +); + +/** @name Unicode normalization + * + * Returns a pointer to newly allocated memory of a NFD, NFC, NFKD, NFKC or + * NFKC_Casefold normalized version of the null-terminated string `str`. These + * are shortcuts to calling @ref utf8proc_map with @ref UTF8PROC_NULLTERM + * combined with @ref UTF8PROC_STABLE and flags indicating the normalization. + */ +/** @{ */ +/** NFD normalization (@ref UTF8PROC_DECOMPOSE). */ +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str); +/** NFC normalization (@ref UTF8PROC_COMPOSE). */ +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str); +/** NFKD normalization (@ref UTF8PROC_DECOMPOSE and @ref UTF8PROC_COMPAT). */ +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str); +/** NFKC normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT). */ +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str); +/** + * NFKC_Casefold normalization (@ref UTF8PROC_COMPOSE and @ref UTF8PROC_COMPAT + * and @ref UTF8PROC_CASEFOLD and @ref UTF8PROC_IGNORE). + **/ +UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC_Casefold(const utf8proc_uint8_t *str); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hilti/src/3rdparty/utf8proc/utf8proc_data.c b/hilti/src/3rdparty/utf8proc/utf8proc_data.c new file mode 100644 index 000000000..8b18c4be5 --- /dev/null +++ b/hilti/src/3rdparty/utf8proc/utf8proc_data.c @@ -0,0 +1,14820 @@ +static const utf8proc_uint16_t utf8proc_sequences[] = { + 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 32, 32, 776, 32, 772, + 50, 51, 32, 769, 956, 32, 807, 49, + 49, 8260, 52, 49, 8260, 50, 51, 8260, + 52, 65, 768, 224, 65, 769, 225, 65, + 770, 226, 65, 771, 227, 65, 776, 228, + 65, 778, 229, 230, 67, 807, 231, 69, + 768, 232, 69, 769, 233, 69, 770, 234, + 69, 776, 235, 73, 768, 236, 73, 769, + 237, 73, 770, 238, 73, 776, 239, 240, + 78, 771, 241, 79, 768, 242, 79, 769, + 243, 79, 770, 244, 79, 771, 245, 79, + 776, 246, 248, 85, 768, 249, 85, 769, + 250, 85, 770, 251, 85, 776, 252, 89, + 769, 253, 254, 115, 115, 97, 768, 97, + 769, 97, 770, 97, 771, 97, 776, 97, + 778, 99, 807, 101, 768, 101, 769, 101, + 770, 101, 776, 105, 768, 105, 769, 105, + 770, 105, 776, 110, 771, 111, 768, 111, + 769, 111, 770, 111, 771, 111, 776, 117, + 768, 117, 769, 117, 770, 117, 776, 121, + 769, 121, 776, 65, 772, 257, 97, 772, + 65, 774, 259, 97, 774, 65, 808, 261, + 97, 808, 67, 769, 263, 99, 769, 67, + 770, 265, 99, 770, 67, 775, 267, 99, + 775, 67, 780, 269, 99, 780, 68, 780, + 271, 100, 780, 273, 69, 772, 275, 101, + 772, 69, 774, 277, 101, 774, 69, 775, + 279, 101, 775, 69, 808, 281, 101, 808, + 69, 780, 283, 101, 780, 71, 770, 285, + 103, 770, 71, 774, 287, 103, 774, 71, + 775, 289, 103, 775, 71, 807, 291, 103, + 807, 72, 770, 293, 104, 770, 295, 73, + 771, 297, 105, 771, 73, 772, 299, 105, + 772, 73, 774, 301, 105, 774, 73, 808, + 303, 105, 808, 73, 775, 105, 775, 73, + 74, 307, 105, 106, 74, 770, 309, 106, + 770, 75, 807, 311, 107, 807, 76, 769, + 314, 108, 769, 76, 807, 316, 108, 807, + 76, 780, 318, 108, 780, 76, 183, 320, + 108, 183, 322, 78, 769, 324, 110, 769, + 78, 807, 326, 110, 807, 78, 780, 328, + 110, 780, 700, 110, 331, 79, 772, 333, + 111, 772, 79, 774, 335, 111, 774, 79, + 779, 337, 111, 779, 339, 82, 769, 341, + 114, 769, 82, 807, 343, 114, 807, 82, + 780, 345, 114, 780, 83, 769, 347, 115, + 769, 83, 770, 349, 115, 770, 83, 807, + 351, 115, 807, 83, 780, 353, 115, 780, + 84, 807, 355, 116, 807, 84, 780, 357, + 116, 780, 359, 85, 771, 361, 117, 771, + 85, 772, 363, 117, 772, 85, 774, 365, + 117, 774, 85, 778, 367, 117, 778, 85, + 779, 369, 117, 779, 85, 808, 371, 117, + 808, 87, 770, 373, 119, 770, 89, 770, + 375, 121, 770, 89, 776, 255, 90, 769, + 378, 122, 769, 90, 775, 380, 122, 775, + 90, 780, 382, 122, 780, 595, 387, 389, + 596, 392, 598, 599, 396, 477, 601, 603, + 402, 608, 611, 617, 616, 409, 623, 626, + 629, 79, 795, 417, 111, 795, 419, 421, + 640, 424, 643, 429, 648, 85, 795, 432, + 117, 795, 650, 651, 436, 438, 658, 441, + 445, 68, 381, 454, 68, 382, 100, 382, + 76, 74, 457, 76, 106, 108, 106, 78, + 74, 460, 78, 106, 110, 106, 65, 780, + 462, 97, 780, 73, 780, 464, 105, 780, + 79, 780, 466, 111, 780, 85, 780, 468, + 117, 780, 220, 772, 470, 252, 772, 220, + 769, 472, 252, 769, 220, 780, 474, 252, + 780, 220, 768, 476, 252, 768, 196, 772, + 479, 228, 772, 550, 772, 481, 551, 772, + 198, 772, 483, 230, 772, 485, 71, 780, + 487, 103, 780, 75, 780, 489, 107, 780, + 79, 808, 491, 111, 808, 490, 772, 493, + 491, 772, 439, 780, 495, 658, 780, 106, + 780, 68, 90, 499, 68, 122, 100, 122, + 71, 769, 501, 103, 769, 405, 447, 78, + 768, 505, 110, 768, 197, 769, 507, 229, + 769, 198, 769, 509, 230, 769, 216, 769, + 511, 248, 769, 65, 783, 513, 97, 783, + 65, 785, 515, 97, 785, 69, 783, 517, + 101, 783, 69, 785, 519, 101, 785, 73, + 783, 521, 105, 783, 73, 785, 523, 105, + 785, 79, 783, 525, 111, 783, 79, 785, + 527, 111, 785, 82, 783, 529, 114, 783, + 82, 785, 531, 114, 785, 85, 783, 533, + 117, 783, 85, 785, 535, 117, 785, 83, + 806, 537, 115, 806, 84, 806, 539, 116, + 806, 541, 72, 780, 543, 104, 780, 414, + 547, 549, 65, 775, 551, 97, 775, 69, + 807, 553, 101, 807, 214, 772, 555, 246, + 772, 213, 772, 557, 245, 772, 79, 775, + 559, 111, 775, 558, 772, 561, 559, 772, + 89, 772, 563, 121, 772, 11365, 572, 410, + 11366, 578, 384, 649, 652, 583, 585, 587, + 589, 591, 614, 633, 635, 641, 32, 774, + 32, 775, 32, 778, 32, 808, 32, 771, + 32, 779, 661, 768, 769, 787, 776, 769, + 953, 881, 883, 697, 887, 32, 837, 59, + 1011, 168, 769, 913, 769, 940, 183, 917, + 769, 941, 919, 769, 942, 921, 769, 943, + 927, 769, 972, 933, 769, 973, 937, 769, + 974, 970, 769, 953, 776, 769, 945, 946, + 947, 948, 949, 950, 951, 952, 954, 955, + 957, 958, 959, 960, 961, 963, 964, 965, + 966, 967, 968, 969, 921, 776, 970, 933, + 776, 971, 945, 769, 949, 769, 951, 769, + 953, 769, 971, 769, 965, 776, 769, 953, + 776, 965, 776, 959, 769, 965, 769, 969, + 769, 983, 933, 978, 769, 978, 776, 985, + 987, 989, 991, 993, 995, 997, 999, 1001, + 1003, 1005, 1007, 962, 920, 1016, 931, 1010, + 1019, 891, 892, 893, 1045, 768, 1104, 1045, + 776, 1105, 1106, 1043, 769, 1107, 1108, 1109, + 1110, 1030, 776, 1111, 1112, 1113, 1114, 1115, + 1050, 769, 1116, 1048, 768, 1117, 1059, 774, + 1118, 1119, 1072, 1073, 1074, 1075, 1076, 1077, + 1078, 1079, 1080, 1048, 774, 1081, 1082, 1083, + 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, + 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, + 1100, 1101, 1102, 1103, 1080, 774, 1077, 768, + 1077, 776, 1075, 769, 1110, 776, 1082, 769, + 1080, 768, 1091, 774, 1121, 1123, 1125, 1127, + 1129, 1131, 1133, 1135, 1137, 1139, 1141, 1140, + 783, 1143, 1141, 783, 1145, 1147, 1149, 1151, + 1153, 1163, 1165, 1167, 1169, 1171, 1173, 1175, + 1177, 1179, 1181, 1183, 1185, 1187, 1189, 1191, + 1193, 1195, 1197, 1199, 1201, 1203, 1205, 1207, + 1209, 1211, 1213, 1215, 1231, 1046, 774, 1218, + 1078, 774, 1220, 1222, 1224, 1226, 1228, 1230, + 1040, 774, 1233, 1072, 774, 1040, 776, 1235, + 1072, 776, 1237, 1045, 774, 1239, 1077, 774, + 1241, 1240, 776, 1243, 1241, 776, 1046, 776, + 1245, 1078, 776, 1047, 776, 1247, 1079, 776, + 1249, 1048, 772, 1251, 1080, 772, 1048, 776, + 1253, 1080, 776, 1054, 776, 1255, 1086, 776, + 1257, 1256, 776, 1259, 1257, 776, 1069, 776, + 1261, 1101, 776, 1059, 772, 1263, 1091, 772, + 1059, 776, 1265, 1091, 776, 1059, 779, 1267, + 1091, 779, 1063, 776, 1269, 1095, 776, 1271, + 1067, 776, 1273, 1099, 776, 1275, 1277, 1279, + 1281, 1283, 1285, 1287, 1289, 1291, 1293, 1295, + 1297, 1299, 1301, 1303, 1305, 1307, 1309, 1311, + 1313, 1315, 1317, 1319, 1321, 1323, 1325, 1327, + 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, + 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, + 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, + 1401, 1402, 1403, 1404, 1405, 1406, 1407, 1408, + 1409, 1410, 1411, 1412, 1413, 1414, 1381, 1410, + 1575, 1619, 1575, 1620, 1608, 1620, 1575, 1621, + 1610, 1620, 1575, 1652, 1608, 1652, 1735, 1652, + 1610, 1652, 1749, 1620, 1729, 1620, 1746, 1620, + 2344, 2364, 2352, 2364, 2355, 2364, 2325, 2364, + 2326, 2364, 2327, 2364, 2332, 2364, 2337, 2364, + 2338, 2364, 2347, 2364, 2351, 2364, 2503, 2494, + 2503, 2519, 2465, 2492, 2466, 2492, 2479, 2492, + 2610, 2620, 2616, 2620, 2582, 2620, 2583, 2620, + 2588, 2620, 2603, 2620, 2887, 2902, 2887, 2878, + 2887, 2903, 2849, 2876, 2850, 2876, 2962, 3031, + 3014, 3006, 3015, 3006, 3014, 3031, 3142, 3158, + 3263, 3285, 3270, 3285, 3270, 3286, 3270, 3266, + 3274, 3285, 3398, 3390, 3399, 3390, 3398, 3415, + 3545, 3530, 3545, 3535, 3548, 3530, 3545, 3551, + 3661, 3634, 3789, 3762, 3755, 3737, 3755, 3745, + 3851, 3906, 4023, 3916, 4023, 3921, 4023, 3926, + 4023, 3931, 4023, 3904, 4021, 3953, 3954, 3953, + 3956, 4018, 3968, 4018, 3969, 4019, 3968, 4019, + 3969, 3953, 3968, 3986, 4023, 3996, 4023, 4001, + 4023, 4006, 4023, 4011, 4023, 3984, 4021, 4133, + 4142, 11520, 11521, 11522, 11523, 11524, 11525, 11526, + 11527, 11528, 11529, 11530, 11531, 11532, 11533, 11534, + 11535, 11536, 11537, 11538, 11539, 11540, 11541, 11542, + 11543, 11544, 11545, 11546, 11547, 11548, 11549, 11550, + 11551, 11552, 11553, 11554, 11555, 11556, 11557, 11559, + 11565, 4316, 5104, 5105, 5106, 5107, 5108, 5109, + 6917, 6965, 6919, 6965, 6921, 6965, 6923, 6965, + 6925, 6965, 6929, 6965, 6970, 6965, 6972, 6965, + 6974, 6965, 6975, 6965, 6978, 6965, 42571, 4304, + 4305, 4306, 4307, 4308, 4309, 4310, 4311, 4312, + 4313, 4314, 4315, 4317, 4318, 4319, 4320, 4321, + 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, + 4330, 4331, 4332, 4333, 4334, 4335, 4336, 4337, + 4338, 4339, 4340, 4341, 4342, 4343, 4344, 4345, + 4346, 4349, 4350, 4351, 65, 198, 66, 68, + 69, 398, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 546, 80, 82, 84, 85, + 87, 592, 593, 7426, 604, 7446, 7447, 7453, + 7461, 594, 597, 607, 609, 613, 618, 7547, + 669, 621, 7557, 671, 625, 624, 627, 628, + 632, 642, 427, 7452, 656, 657, 65, 805, + 7681, 97, 805, 66, 775, 7683, 98, 775, + 66, 803, 7685, 98, 803, 66, 817, 7687, + 98, 817, 199, 769, 7689, 231, 769, 68, + 775, 7691, 100, 775, 68, 803, 7693, 100, + 803, 68, 817, 7695, 100, 817, 68, 807, + 7697, 100, 807, 68, 813, 7699, 100, 813, + 274, 768, 7701, 275, 768, 274, 769, 7703, + 275, 769, 69, 813, 7705, 101, 813, 69, + 816, 7707, 101, 816, 552, 774, 7709, 553, + 774, 70, 775, 7711, 102, 775, 71, 772, + 7713, 103, 772, 72, 775, 7715, 104, 775, + 72, 803, 7717, 104, 803, 72, 776, 7719, + 104, 776, 72, 807, 7721, 104, 807, 72, + 814, 7723, 104, 814, 73, 816, 7725, 105, + 816, 207, 769, 7727, 239, 769, 75, 769, + 7729, 107, 769, 75, 803, 7731, 107, 803, + 75, 817, 7733, 107, 817, 76, 803, 7735, + 108, 803, 7734, 772, 7737, 7735, 772, 76, + 817, 7739, 108, 817, 76, 813, 7741, 108, + 813, 77, 769, 7743, 109, 769, 77, 775, + 7745, 109, 775, 77, 803, 7747, 109, 803, + 78, 775, 7749, 110, 775, 78, 803, 7751, + 110, 803, 78, 817, 7753, 110, 817, 78, + 813, 7755, 110, 813, 213, 769, 7757, 245, + 769, 213, 776, 7759, 245, 776, 332, 768, + 7761, 333, 768, 332, 769, 7763, 333, 769, + 80, 769, 7765, 112, 769, 80, 775, 7767, + 112, 775, 82, 775, 7769, 114, 775, 82, + 803, 7771, 114, 803, 7770, 772, 7773, 7771, + 772, 82, 817, 7775, 114, 817, 83, 775, + 7777, 115, 775, 83, 803, 7779, 115, 803, + 346, 775, 7781, 347, 775, 352, 775, 7783, + 353, 775, 7778, 775, 7785, 7779, 775, 84, + 775, 7787, 116, 775, 84, 803, 7789, 116, + 803, 84, 817, 7791, 116, 817, 84, 813, + 7793, 116, 813, 85, 804, 7795, 117, 804, + 85, 816, 7797, 117, 816, 85, 813, 7799, + 117, 813, 360, 769, 7801, 361, 769, 362, + 776, 7803, 363, 776, 86, 771, 7805, 118, + 771, 86, 803, 7807, 118, 803, 87, 768, + 7809, 119, 768, 87, 769, 7811, 119, 769, + 87, 776, 7813, 119, 776, 87, 775, 7815, + 119, 775, 87, 803, 7817, 119, 803, 88, + 775, 7819, 120, 775, 88, 776, 7821, 120, + 776, 89, 775, 7823, 121, 775, 90, 770, + 7825, 122, 770, 90, 803, 7827, 122, 803, + 90, 817, 7829, 122, 817, 104, 817, 116, + 776, 119, 778, 121, 778, 97, 702, 383, + 775, 65, 803, 7841, 97, 803, 65, 777, + 7843, 97, 777, 194, 769, 7845, 226, 769, + 194, 768, 7847, 226, 768, 194, 777, 7849, + 226, 777, 194, 771, 7851, 226, 771, 7840, + 770, 7853, 7841, 770, 258, 769, 7855, 259, + 769, 258, 768, 7857, 259, 768, 258, 777, + 7859, 259, 777, 258, 771, 7861, 259, 771, + 7840, 774, 7863, 7841, 774, 69, 803, 7865, + 101, 803, 69, 777, 7867, 101, 777, 69, + 771, 7869, 101, 771, 202, 769, 7871, 234, + 769, 202, 768, 7873, 234, 768, 202, 777, + 7875, 234, 777, 202, 771, 7877, 234, 771, + 7864, 770, 7879, 7865, 770, 73, 777, 7881, + 105, 777, 73, 803, 7883, 105, 803, 79, + 803, 7885, 111, 803, 79, 777, 7887, 111, + 777, 212, 769, 7889, 244, 769, 212, 768, + 7891, 244, 768, 212, 777, 7893, 244, 777, + 212, 771, 7895, 244, 771, 7884, 770, 7897, + 7885, 770, 416, 769, 7899, 417, 769, 416, + 768, 7901, 417, 768, 416, 777, 7903, 417, + 777, 416, 771, 7905, 417, 771, 416, 803, + 7907, 417, 803, 85, 803, 7909, 117, 803, + 85, 777, 7911, 117, 777, 431, 769, 7913, + 432, 769, 431, 768, 7915, 432, 768, 431, + 777, 7917, 432, 777, 431, 771, 7919, 432, + 771, 431, 803, 7921, 432, 803, 89, 768, + 7923, 121, 768, 89, 803, 7925, 121, 803, + 89, 777, 7927, 121, 777, 89, 771, 7929, + 121, 771, 7931, 7933, 7935, 945, 787, 945, + 788, 7936, 768, 7937, 768, 7936, 769, 7937, + 769, 7936, 834, 7937, 834, 913, 787, 7936, + 913, 788, 7937, 7944, 768, 7938, 7945, 768, + 7939, 7944, 769, 7940, 7945, 769, 7941, 7944, + 834, 7942, 7945, 834, 7943, 949, 787, 949, + 788, 7952, 768, 7953, 768, 7952, 769, 7953, + 769, 917, 787, 7952, 917, 788, 7953, 7960, + 768, 7954, 7961, 768, 7955, 7960, 769, 7956, + 7961, 769, 7957, 951, 787, 951, 788, 7968, + 768, 7969, 768, 7968, 769, 7969, 769, 7968, + 834, 7969, 834, 919, 787, 7968, 919, 788, + 7969, 7976, 768, 7970, 7977, 768, 7971, 7976, + 769, 7972, 7977, 769, 7973, 7976, 834, 7974, + 7977, 834, 7975, 953, 787, 953, 788, 7984, + 768, 7985, 768, 7984, 769, 7985, 769, 7984, + 834, 7985, 834, 921, 787, 7984, 921, 788, + 7985, 7992, 768, 7986, 7993, 768, 7987, 7992, + 769, 7988, 7993, 769, 7989, 7992, 834, 7990, + 7993, 834, 7991, 959, 787, 959, 788, 8000, + 768, 8001, 768, 8000, 769, 8001, 769, 927, + 787, 8000, 927, 788, 8001, 8008, 768, 8002, + 8009, 768, 8003, 8008, 769, 8004, 8009, 769, + 8005, 965, 787, 965, 788, 8016, 768, 965, + 787, 768, 8017, 768, 8016, 769, 965, 787, + 769, 8017, 769, 8016, 834, 965, 787, 834, + 8017, 834, 933, 788, 8017, 8025, 768, 8019, + 8025, 769, 8021, 8025, 834, 8023, 969, 787, + 969, 788, 8032, 768, 8033, 768, 8032, 769, + 8033, 769, 8032, 834, 8033, 834, 937, 787, + 8032, 937, 788, 8033, 8040, 768, 8034, 8041, + 768, 8035, 8040, 769, 8036, 8041, 769, 8037, + 8040, 834, 8038, 8041, 834, 8039, 945, 768, + 949, 768, 951, 768, 953, 768, 959, 768, + 965, 768, 969, 768, 7936, 837, 7936, 953, + 7937, 837, 7937, 953, 7938, 837, 7938, 953, + 7939, 837, 7939, 953, 7940, 837, 7940, 953, + 7941, 837, 7941, 953, 7942, 837, 7942, 953, + 7943, 837, 7943, 953, 7944, 837, 7945, 837, + 7946, 837, 7947, 837, 7948, 837, 7949, 837, + 7950, 837, 7951, 837, 7968, 837, 7968, 953, + 7969, 837, 7969, 953, 7970, 837, 7970, 953, + 7971, 837, 7971, 953, 7972, 837, 7972, 953, + 7973, 837, 7973, 953, 7974, 837, 7974, 953, + 7975, 837, 7975, 953, 7976, 837, 7977, 837, + 7978, 837, 7979, 837, 7980, 837, 7981, 837, + 7982, 837, 7983, 837, 8032, 837, 8032, 953, + 8033, 837, 8033, 953, 8034, 837, 8034, 953, + 8035, 837, 8035, 953, 8036, 837, 8036, 953, + 8037, 837, 8037, 953, 8038, 837, 8038, 953, + 8039, 837, 8039, 953, 8040, 837, 8041, 837, + 8042, 837, 8043, 837, 8044, 837, 8045, 837, + 8046, 837, 8047, 837, 945, 774, 945, 772, + 8048, 837, 8048, 953, 945, 837, 945, 953, + 940, 837, 940, 953, 945, 834, 8118, 837, + 945, 834, 953, 913, 774, 8112, 913, 772, + 8113, 913, 768, 8048, 902, 8049, 913, 837, + 32, 787, 32, 834, 168, 834, 8052, 837, + 8052, 953, 951, 837, 951, 953, 942, 837, + 942, 953, 951, 834, 8134, 837, 951, 834, + 953, 917, 768, 8050, 904, 8051, 919, 768, + 8052, 905, 8053, 919, 837, 8127, 768, 8127, + 769, 8127, 834, 953, 774, 953, 772, 970, + 768, 953, 776, 768, 912, 953, 834, 970, + 834, 953, 776, 834, 921, 774, 8144, 921, + 772, 8145, 921, 768, 8054, 906, 8055, 8190, + 768, 8190, 769, 8190, 834, 965, 774, 965, + 772, 971, 768, 965, 776, 768, 944, 961, + 787, 961, 788, 965, 834, 971, 834, 965, + 776, 834, 933, 774, 8160, 933, 772, 8161, + 933, 768, 8058, 910, 8059, 929, 788, 8165, + 168, 768, 901, 96, 8060, 837, 8060, 953, + 969, 837, 969, 953, 974, 837, 974, 953, + 969, 834, 8182, 837, 969, 834, 953, 927, + 768, 8056, 908, 8057, 937, 768, 8060, 911, + 8061, 937, 837, 180, 32, 788, 8194, 8195, + 8208, 32, 819, 46, 46, 46, 46, 46, + 46, 8242, 8242, 8242, 8242, 8242, 8245, 8245, + 8245, 8245, 8245, 33, 33, 32, 773, 63, + 63, 63, 33, 33, 63, 8242, 8242, 8242, + 8242, 48, 52, 53, 54, 55, 56, 57, + 43, 8722, 61, 40, 41, 82, 115, 97, + 47, 99, 97, 47, 115, 67, 176, 67, + 99, 47, 111, 99, 47, 117, 400, 176, + 70, 78, 111, 81, 83, 77, 84, 69, + 76, 84, 77, 90, 937, 197, 70, 8526, + 1488, 1489, 1490, 1491, 70, 65, 88, 915, + 928, 8721, 49, 8260, 55, 49, 8260, 57, + 49, 8260, 49, 48, 49, 8260, 51, 50, + 8260, 51, 49, 8260, 53, 50, 8260, 53, + 51, 8260, 53, 52, 8260, 53, 49, 8260, + 54, 53, 8260, 54, 49, 8260, 56, 51, + 8260, 56, 53, 8260, 56, 55, 8260, 56, + 49, 8260, 8560, 73, 73, 8561, 73, 73, + 73, 8562, 73, 86, 8563, 86, 8564, 86, + 73, 8565, 86, 73, 73, 8566, 86, 73, + 73, 73, 8567, 73, 88, 8568, 88, 8569, + 88, 73, 8570, 88, 73, 73, 8571, 8572, + 8573, 8574, 8575, 105, 105, 105, 105, 105, + 105, 118, 118, 105, 118, 105, 105, 118, + 105, 105, 105, 105, 120, 120, 105, 120, + 105, 105, 8580, 48, 8260, 51, 8592, 824, + 8594, 824, 8596, 824, 8656, 824, 8660, 824, + 8658, 824, 8707, 824, 8712, 824, 8715, 824, + 8739, 824, 8741, 824, 8747, 8747, 8747, 8747, + 8747, 8750, 8750, 8750, 8750, 8750, 8764, 824, + 8771, 824, 8773, 824, 8776, 824, 61, 824, + 8801, 824, 8781, 824, 60, 824, 62, 824, + 8804, 824, 8805, 824, 8818, 824, 8819, 824, + 8822, 824, 8823, 824, 8826, 824, 8827, 824, + 8834, 824, 8835, 824, 8838, 824, 8839, 824, + 8866, 824, 8872, 824, 8873, 824, 8875, 824, + 8828, 824, 8829, 824, 8849, 824, 8850, 824, + 8882, 824, 8883, 824, 8884, 824, 8885, 824, + 12296, 12297, 49, 48, 49, 49, 49, 50, + 49, 51, 49, 52, 49, 53, 49, 54, + 49, 55, 49, 56, 49, 57, 50, 48, + 40, 49, 41, 40, 50, 41, 40, 51, + 41, 40, 52, 41, 40, 53, 41, 40, + 54, 41, 40, 55, 41, 40, 56, 41, + 40, 57, 41, 40, 49, 48, 41, 40, + 49, 49, 41, 40, 49, 50, 41, 40, + 49, 51, 41, 40, 49, 52, 41, 40, + 49, 53, 41, 40, 49, 54, 41, 40, + 49, 55, 41, 40, 49, 56, 41, 40, + 49, 57, 41, 40, 50, 48, 41, 49, + 46, 50, 46, 51, 46, 52, 46, 53, + 46, 54, 46, 55, 46, 56, 46, 57, + 46, 49, 48, 46, 49, 49, 46, 49, + 50, 46, 49, 51, 46, 49, 52, 46, + 49, 53, 46, 49, 54, 46, 49, 55, + 46, 49, 56, 46, 49, 57, 46, 50, + 48, 46, 40, 97, 41, 40, 98, 41, + 40, 99, 41, 40, 100, 41, 40, 101, + 41, 40, 102, 41, 40, 103, 41, 40, + 104, 41, 40, 105, 41, 40, 106, 41, + 40, 107, 41, 40, 108, 41, 40, 109, + 41, 40, 110, 41, 40, 111, 41, 40, + 112, 41, 40, 113, 41, 40, 114, 41, + 40, 115, 41, 40, 116, 41, 40, 117, + 41, 40, 118, 41, 40, 119, 41, 40, + 120, 41, 40, 121, 41, 40, 122, 41, + 9424, 9425, 9426, 9427, 9428, 9429, 9430, 9431, + 9432, 9433, 9434, 9435, 9436, 9437, 9438, 9439, + 9440, 9441, 83, 9442, 9443, 9444, 9445, 9446, + 9447, 89, 9448, 9449, 8747, 8747, 8747, 8747, + 58, 58, 61, 61, 61, 61, 61, 61, + 10973, 824, 11312, 11313, 11314, 11315, 11316, 11317, + 11318, 11319, 11320, 11321, 11322, 11323, 11324, 11325, + 11326, 11327, 11328, 11329, 11330, 11331, 11332, 11333, + 11334, 11335, 11336, 11337, 11338, 11339, 11340, 11341, + 11342, 11343, 11344, 11345, 11346, 11347, 11348, 11349, + 11350, 11351, 11352, 11353, 11354, 11355, 11356, 11357, + 11358, 11361, 619, 7549, 637, 11368, 11370, 11372, + 11379, 11382, 575, 576, 11393, 11395, 11397, 11399, + 11401, 11403, 11405, 11407, 11409, 11411, 11413, 11415, + 11417, 11419, 11421, 11423, 11425, 11427, 11429, 11431, + 11433, 11435, 11437, 11439, 11441, 11443, 11445, 11447, + 11449, 11451, 11453, 11455, 11457, 11459, 11461, 11463, + 11465, 11467, 11469, 11471, 11473, 11475, 11477, 11479, + 11481, 11483, 11485, 11487, 11489, 11491, 11500, 11502, + 11507, 11617, 27597, 40863, 19968, 20008, 20022, 20031, + 20057, 20101, 20108, 20128, 20154, 20799, 20837, 20843, + 20866, 20886, 20907, 20960, 20981, 20992, 21147, 21241, + 21269, 21274, 21304, 21313, 21340, 21353, 21378, 21430, + 21448, 21475, 22231, 22303, 22763, 22786, 22794, 22805, + 22823, 22899, 23376, 23424, 23544, 23567, 23586, 23608, + 23662, 23665, 24027, 24037, 24049, 24062, 24178, 24186, + 24191, 24308, 24318, 24331, 24339, 24400, 24417, 24435, + 24515, 25096, 25142, 25163, 25903, 25908, 25991, 26007, + 26020, 26041, 26080, 26085, 26352, 26376, 26408, 27424, + 27490, 27513, 27571, 27595, 27604, 27611, 27663, 27668, + 27700, 28779, 29226, 29238, 29243, 29247, 29255, 29273, + 29275, 29356, 29572, 29577, 29916, 29926, 29976, 29983, + 29992, 30000, 30091, 30098, 30326, 30333, 30382, 30399, + 30446, 30683, 30690, 30707, 31034, 31160, 31166, 31348, + 31435, 31481, 31859, 31992, 32566, 32593, 32650, 32701, + 32769, 32780, 32786, 32819, 32895, 32905, 33251, 33258, + 33267, 33276, 33292, 33307, 33311, 33390, 33394, 33400, + 34381, 34411, 34880, 34892, 34915, 35198, 35211, 35282, + 35328, 35895, 35910, 35925, 35960, 35997, 36196, 36208, + 36275, 36523, 36554, 36763, 36784, 36789, 37009, 37193, + 37318, 37324, 37329, 38263, 38272, 38428, 38582, 38585, + 38632, 38737, 38750, 38754, 38761, 38859, 38893, 38899, + 38913, 39080, 39131, 39135, 39318, 39321, 39340, 39592, + 39640, 39647, 39717, 39727, 39730, 39740, 39770, 40165, + 40565, 40575, 40613, 40635, 40643, 40653, 40657, 40697, + 40701, 40718, 40723, 40736, 40763, 40778, 40786, 40845, + 40860, 40864, 12306, 21316, 21317, 12363, 12441, 12365, + 12441, 12367, 12441, 12369, 12441, 12371, 12441, 12373, + 12441, 12375, 12441, 12377, 12441, 12379, 12441, 12381, + 12441, 12383, 12441, 12385, 12441, 12388, 12441, 12390, + 12441, 12392, 12441, 12399, 12441, 12399, 12442, 12402, + 12441, 12402, 12442, 12405, 12441, 12405, 12442, 12408, + 12441, 12408, 12442, 12411, 12441, 12411, 12442, 12358, + 12441, 32, 12441, 32, 12442, 12445, 12441, 12424, + 12426, 12459, 12441, 12461, 12441, 12463, 12441, 12465, + 12441, 12467, 12441, 12469, 12441, 12471, 12441, 12473, + 12441, 12475, 12441, 12477, 12441, 12479, 12441, 12481, + 12441, 12484, 12441, 12486, 12441, 12488, 12441, 12495, + 12441, 12495, 12442, 12498, 12441, 12498, 12442, 12501, + 12441, 12501, 12442, 12504, 12441, 12504, 12442, 12507, + 12441, 12507, 12442, 12454, 12441, 12527, 12441, 12528, + 12441, 12529, 12441, 12530, 12441, 12541, 12441, 12467, + 12488, 4352, 4353, 4522, 4354, 4524, 4525, 4355, + 4356, 4357, 4528, 4529, 4530, 4531, 4532, 4533, + 4378, 4358, 4359, 4360, 4385, 4361, 4362, 4363, + 4364, 4365, 4366, 4367, 4368, 4369, 4370, 4449, + 4450, 4451, 4452, 4453, 4454, 4455, 4456, 4457, + 4458, 4459, 4460, 4461, 4462, 4463, 4464, 4465, + 4466, 4467, 4468, 4469, 4448, 4372, 4373, 4551, + 4552, 4556, 4558, 4563, 4567, 4569, 4380, 4573, + 4575, 4381, 4382, 4384, 4386, 4387, 4391, 4393, + 4395, 4396, 4397, 4398, 4399, 4402, 4406, 4416, + 4423, 4428, 4593, 4594, 4439, 4440, 4441, 4484, + 4485, 4488, 4497, 4498, 4500, 4510, 4513, 19977, + 22235, 19978, 20013, 19979, 30002, 19993, 19969, 22825, + 22320, 40, 4352, 41, 40, 4354, 41, 40, + 4355, 41, 40, 4357, 41, 40, 4358, 41, + 40, 4359, 41, 40, 4361, 41, 40, 4363, + 41, 40, 4364, 41, 40, 4366, 41, 40, + 4367, 41, 40, 4368, 41, 40, 4369, 41, + 40, 4370, 41, 40, 4352, 4449, 41, 40, + 4354, 4449, 41, 40, 4355, 4449, 41, 40, + 4357, 4449, 41, 40, 4358, 4449, 41, 40, + 4359, 4449, 41, 40, 4361, 4449, 41, 40, + 4363, 4449, 41, 40, 4364, 4449, 41, 40, + 4366, 4449, 41, 40, 4367, 4449, 41, 40, + 4368, 4449, 41, 40, 4369, 4449, 41, 40, + 4370, 4449, 41, 40, 4364, 4462, 41, 40, + 4363, 4457, 4364, 4453, 4523, 41, 40, 4363, + 4457, 4370, 4462, 41, 40, 19968, 41, 40, + 20108, 41, 40, 19977, 41, 40, 22235, 41, + 40, 20116, 41, 40, 20845, 41, 40, 19971, + 41, 40, 20843, 41, 40, 20061, 41, 40, + 21313, 41, 40, 26376, 41, 40, 28779, 41, + 40, 27700, 41, 40, 26408, 41, 40, 37329, + 41, 40, 22303, 41, 40, 26085, 41, 40, + 26666, 41, 40, 26377, 41, 40, 31038, 41, + 40, 21517, 41, 40, 29305, 41, 40, 36001, + 41, 40, 31069, 41, 40, 21172, 41, 40, + 20195, 41, 40, 21628, 41, 40, 23398, 41, + 40, 30435, 41, 40, 20225, 41, 40, 36039, + 41, 40, 21332, 41, 40, 31085, 41, 40, + 20241, 41, 40, 33258, 41, 40, 33267, 41, + 21839, 24188, 31631, 80, 84, 69, 50, 49, + 50, 50, 50, 51, 50, 52, 50, 53, + 50, 54, 50, 55, 50, 56, 50, 57, + 51, 48, 51, 49, 51, 50, 51, 51, + 51, 52, 51, 53, 4352, 4449, 4354, 4449, + 4355, 4449, 4357, 4449, 4358, 4449, 4359, 4449, + 4361, 4449, 4363, 4449, 4364, 4449, 4366, 4449, + 4367, 4449, 4368, 4449, 4369, 4449, 4370, 4449, + 4366, 4449, 4535, 4352, 4457, 4364, 4462, 4363, + 4468, 4363, 4462, 20116, 20845, 19971, 20061, 26666, + 26377, 31038, 21517, 29305, 36001, 31069, 21172, 31192, + 30007, 36969, 20778, 21360, 27880, 38917, 20241, 20889, + 27491, 24038, 21491, 21307, 23447, 23398, 30435, 20225, + 36039, 21332, 22812, 51, 54, 51, 55, 51, + 56, 51, 57, 52, 48, 52, 49, 52, + 50, 52, 51, 52, 52, 52, 53, 52, + 54, 52, 55, 52, 56, 52, 57, 53, + 48, 49, 26376, 50, 26376, 51, 26376, 52, + 26376, 53, 26376, 54, 26376, 55, 26376, 56, + 26376, 57, 26376, 49, 48, 26376, 49, 49, + 26376, 49, 50, 26376, 72, 103, 101, 114, + 103, 101, 86, 76, 84, 68, 12450, 12452, + 12454, 12456, 12458, 12459, 12461, 12463, 12465, 12467, + 12469, 12471, 12473, 12475, 12477, 12479, 12481, 12484, + 12486, 12488, 12490, 12491, 12492, 12493, 12494, 12495, + 12498, 12501, 12504, 12507, 12510, 12511, 12512, 12513, + 12514, 12516, 12518, 12520, 12521, 12522, 12523, 12524, + 12525, 12527, 12528, 12529, 12530, 20196, 21644, 12450, + 12497, 12540, 12488, 12450, 12523, 12501, 12449, 12450, + 12531, 12506, 12450, 12450, 12540, 12523, 12452, 12491, + 12531, 12464, 12452, 12531, 12481, 12454, 12457, 12531, + 12456, 12473, 12463, 12540, 12489, 12456, 12540, 12459, + 12540, 12458, 12531, 12473, 12458, 12540, 12512, 12459, + 12452, 12522, 12459, 12521, 12483, 12488, 12459, 12525, + 12522, 12540, 12460, 12525, 12531, 12460, 12531, 12510, + 12462, 12460, 12462, 12491, 12540, 12461, 12517, 12522, + 12540, 12462, 12523, 12480, 12540, 12461, 12525, 12461, + 12525, 12464, 12521, 12512, 12461, 12525, 12513, 12540, + 12488, 12523, 12461, 12525, 12527, 12483, 12488, 12464, + 12521, 12512, 12464, 12521, 12512, 12488, 12531, 12463, + 12523, 12476, 12452, 12525, 12463, 12525, 12540, 12493, + 12465, 12540, 12473, 12467, 12523, 12490, 12467, 12540, + 12509, 12469, 12452, 12463, 12523, 12469, 12531, 12481, + 12540, 12512, 12471, 12522, 12531, 12464, 12475, 12531, + 12481, 12475, 12531, 12488, 12480, 12540, 12473, 12487, + 12471, 12489, 12523, 12488, 12531, 12490, 12494, 12494, + 12483, 12488, 12495, 12452, 12484, 12497, 12540, 12475, + 12531, 12488, 12497, 12540, 12484, 12496, 12540, 12524, + 12523, 12500, 12450, 12473, 12488, 12523, 12500, 12463, + 12523, 12500, 12467, 12499, 12523, 12501, 12449, 12521, + 12483, 12489, 12501, 12451, 12540, 12488, 12502, 12483, + 12471, 12455, 12523, 12501, 12521, 12531, 12504, 12463, + 12479, 12540, 12523, 12506, 12477, 12506, 12491, 12498, + 12504, 12523, 12484, 12506, 12531, 12473, 12506, 12540, + 12472, 12505, 12540, 12479, 12509, 12452, 12531, 12488, + 12508, 12523, 12488, 12507, 12531, 12509, 12531, 12489, + 12507, 12540, 12523, 12507, 12540, 12531, 12510, 12452, + 12463, 12525, 12510, 12452, 12523, 12510, 12483, 12495, + 12510, 12523, 12463, 12510, 12531, 12471, 12519, 12531, + 12511, 12463, 12525, 12531, 12511, 12522, 12511, 12522, + 12496, 12540, 12523, 12513, 12460, 12513, 12460, 12488, + 12531, 12513, 12540, 12488, 12523, 12516, 12540, 12489, + 12516, 12540, 12523, 12518, 12450, 12531, 12522, 12483, + 12488, 12523, 12522, 12521, 12523, 12500, 12540, 12523, + 12540, 12502, 12523, 12524, 12512, 12524, 12531, 12488, + 12466, 12531, 12527, 12483, 12488, 48, 28857, 49, + 28857, 50, 28857, 51, 28857, 52, 28857, 53, + 28857, 54, 28857, 55, 28857, 56, 28857, 57, + 28857, 49, 48, 28857, 49, 49, 28857, 49, + 50, 28857, 49, 51, 28857, 49, 52, 28857, + 49, 53, 28857, 49, 54, 28857, 49, 55, + 28857, 49, 56, 28857, 49, 57, 28857, 50, + 48, 28857, 50, 49, 28857, 50, 50, 28857, + 50, 51, 28857, 50, 52, 28857, 104, 80, + 97, 100, 97, 65, 85, 98, 97, 114, + 111, 86, 112, 99, 100, 109, 100, 109, + 178, 100, 109, 179, 73, 85, 24179, 25104, + 26157, 21644, 22823, 27491, 26126, 27835, 26666, 24335, + 20250, 31038, 112, 65, 110, 65, 956, 65, + 109, 65, 107, 65, 75, 66, 77, 66, + 71, 66, 99, 97, 108, 107, 99, 97, + 108, 112, 70, 110, 70, 956, 70, 956, + 103, 109, 103, 107, 103, 72, 122, 107, + 72, 122, 77, 72, 122, 71, 72, 122, + 84, 72, 122, 956, 8467, 109, 8467, 100, + 8467, 107, 8467, 102, 109, 110, 109, 956, + 109, 109, 109, 99, 109, 107, 109, 109, + 109, 178, 99, 109, 178, 109, 178, 107, + 109, 178, 109, 109, 179, 99, 109, 179, + 109, 179, 107, 109, 179, 109, 8725, 115, + 109, 8725, 115, 178, 80, 97, 107, 80, + 97, 77, 80, 97, 71, 80, 97, 114, + 97, 100, 114, 97, 100, 8725, 115, 114, + 97, 100, 8725, 115, 178, 112, 115, 110, + 115, 956, 115, 109, 115, 112, 86, 110, + 86, 956, 86, 109, 86, 107, 86, 77, + 86, 112, 87, 110, 87, 956, 87, 109, + 87, 107, 87, 77, 87, 107, 937, 77, + 937, 97, 46, 109, 46, 66, 113, 99, + 99, 99, 100, 67, 8725, 107, 103, 67, + 111, 46, 100, 66, 71, 121, 104, 97, + 72, 80, 105, 110, 75, 75, 75, 77, + 107, 116, 108, 109, 108, 110, 108, 111, + 103, 108, 120, 109, 98, 109, 105, 108, + 109, 111, 108, 80, 72, 112, 46, 109, + 46, 80, 80, 77, 80, 82, 115, 114, + 83, 118, 87, 98, 86, 8725, 109, 65, + 8725, 109, 49, 26085, 50, 26085, 51, 26085, + 52, 26085, 53, 26085, 54, 26085, 55, 26085, + 56, 26085, 57, 26085, 49, 48, 26085, 49, + 49, 26085, 49, 50, 26085, 49, 51, 26085, + 49, 52, 26085, 49, 53, 26085, 49, 54, + 26085, 49, 55, 26085, 49, 56, 26085, 49, + 57, 26085, 50, 48, 26085, 50, 49, 26085, + 50, 50, 26085, 50, 51, 26085, 50, 52, + 26085, 50, 53, 26085, 50, 54, 26085, 50, + 55, 26085, 50, 56, 26085, 50, 57, 26085, + 51, 48, 26085, 51, 49, 26085, 103, 97, + 108, 42561, 42563, 42565, 42567, 42569, 42573, 42575, + 42577, 42579, 42581, 42583, 42585, 42587, 42589, 42591, + 42593, 42595, 42597, 42599, 42601, 42603, 42605, 42625, + 42627, 42629, 42631, 42633, 42635, 42637, 42639, 42641, + 42643, 42645, 42647, 42649, 42651, 42787, 42789, 42791, + 42793, 42795, 42797, 42799, 42803, 42805, 42807, 42809, + 42811, 42813, 42815, 42817, 42819, 42821, 42823, 42825, + 42827, 42829, 42831, 42833, 42835, 42837, 42839, 42841, + 42843, 42845, 42847, 42849, 42851, 42853, 42855, 42857, + 42859, 42861, 42863, 42874, 42876, 7545, 42879, 42881, + 42883, 42885, 42887, 42892, 42897, 42899, 42903, 42905, + 42907, 42909, 42911, 42913, 42915, 42917, 42919, 42921, + 620, 670, 647, 43859, 42933, 42935, 42937, 42939, + 42941, 42943, 42947, 42900, 7566, 294, 43831, 43858, + 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, + 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, + 5040, 5041, 5042, 5043, 5044, 5045, 5046, 5047, + 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055, + 5056, 5057, 5058, 5059, 5060, 5061, 5062, 5063, + 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, + 5072, 5073, 5074, 5075, 5076, 5077, 5078, 5079, + 5080, 5081, 5082, 5083, 5084, 5085, 5086, 5087, + 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, + 5096, 5097, 5098, 5099, 5100, 5101, 5102, 5103, + 35912, 26356, 36040, 28369, 20018, 21477, 22865, 21895, + 22856, 25078, 30313, 32645, 34367, 34746, 35064, 37007, + 27138, 27931, 28889, 29662, 33853, 37226, 39409, 20098, + 21365, 27396, 29211, 34349, 40478, 23888, 28651, 34253, + 35172, 25289, 33240, 34847, 24266, 26391, 28010, 29436, + 37070, 20358, 20919, 21214, 25796, 27347, 29200, 30439, + 34310, 34396, 36335, 38706, 39791, 40442, 30860, 31103, + 32160, 33737, 37636, 35542, 22751, 24324, 31840, 32894, + 29282, 30922, 36034, 38647, 22744, 23650, 27155, 28122, + 28431, 32047, 32311, 38475, 21202, 32907, 20956, 20940, + 31260, 32190, 33777, 38517, 35712, 25295, 35582, 20025, + 23527, 24594, 29575, 30064, 21271, 30971, 20415, 24489, + 19981, 27852, 25976, 32034, 21443, 22622, 30465, 33865, + 35498, 27578, 27784, 25342, 33509, 25504, 30053, 20142, + 20841, 20937, 26753, 31975, 33391, 35538, 37327, 21237, + 21570, 24300, 26053, 28670, 31018, 38317, 39530, 40599, + 40654, 26310, 27511, 36706, 24180, 24976, 25088, 25754, + 28451, 29001, 29833, 31178, 32244, 32879, 36646, 34030, + 36899, 37706, 21015, 21155, 21693, 28872, 35010, 24265, + 24565, 25467, 27566, 31806, 29557, 20196, 22265, 23994, + 24604, 29618, 29801, 32666, 32838, 37428, 38646, 38728, + 38936, 20363, 31150, 37300, 38584, 24801, 20102, 20698, + 23534, 23615, 26009, 29134, 30274, 34044, 36988, 26248, + 38446, 21129, 26491, 26611, 27969, 28316, 29705, 30041, + 30827, 32016, 39006, 25134, 38520, 20523, 23833, 28138, + 36650, 24459, 24900, 26647, 38534, 21033, 21519, 23653, + 26131, 26446, 26792, 27877, 29702, 30178, 32633, 35023, + 35041, 38626, 21311, 28346, 21533, 29136, 29848, 34298, + 38563, 40023, 40607, 26519, 28107, 33256, 31520, 31890, + 29376, 28825, 35672, 20160, 33590, 21050, 20999, 24230, + 25299, 31958, 23429, 27934, 26292, 36667, 38477, 24275, + 20800, 21952, 22618, 26228, 20958, 29482, 30410, 31036, + 31070, 31077, 31119, 38742, 31934, 34322, 35576, 36920, + 37117, 39151, 39164, 39208, 40372, 37086, 38583, 20398, + 20711, 20813, 21193, 21220, 21329, 21917, 22022, 22120, + 22592, 22696, 23652, 24724, 24936, 24974, 25074, 25935, + 26082, 26257, 26757, 28023, 28186, 28450, 29038, 29227, + 29730, 30865, 31049, 31048, 31056, 31062, 31117, 31118, + 31296, 31361, 31680, 32265, 32321, 32626, 32773, 33261, + 33401, 33879, 35088, 35222, 35585, 35641, 36051, 36104, + 36790, 38627, 38911, 38971, 24693, 55376, 57070, 33304, + 20006, 20917, 20840, 20352, 20805, 20864, 21191, 21242, + 21845, 21913, 21986, 22707, 22852, 22868, 23138, 23336, + 24274, 24281, 24425, 24493, 24792, 24910, 24840, 24928, + 25140, 25540, 25628, 25682, 25942, 26395, 26454, 28379, + 28363, 28702, 30631, 29237, 29359, 29809, 29958, 30011, + 30237, 30239, 30427, 30452, 30538, 30528, 30924, 31409, + 31867, 32091, 32574, 33618, 33775, 34681, 35137, 35206, + 35519, 35531, 35565, 35722, 36664, 36978, 37273, 37494, + 38524, 38875, 38923, 39698, 55370, 56394, 55370, 56388, + 55372, 57301, 15261, 16408, 16441, 55380, 56905, 55383, + 56528, 55391, 57043, 40771, 40846, 102, 102, 102, + 105, 102, 108, 102, 102, 105, 102, 102, + 108, 383, 116, 115, 116, 1396, 1398, 1396, + 1381, 1396, 1387, 1406, 1398, 1396, 1389, 1497, + 1460, 1522, 1463, 1506, 1492, 1499, 1500, 1501, + 1512, 1514, 1513, 1473, 1513, 1474, 64329, 1473, + 64329, 1474, 1488, 1463, 1488, 1464, 1488, 1468, + 1489, 1468, 1490, 1468, 1491, 1468, 1492, 1468, + 1493, 1468, 1494, 1468, 1496, 1468, 1497, 1468, + 1498, 1468, 1499, 1468, 1500, 1468, 1502, 1468, + 1504, 1468, 1505, 1468, 1507, 1468, 1508, 1468, + 1510, 1468, 1511, 1468, 1512, 1468, 1513, 1468, + 1514, 1468, 1493, 1465, 1489, 1471, 1499, 1471, + 1508, 1471, 1488, 1500, 1649, 1659, 1662, 1664, + 1658, 1663, 1657, 1700, 1702, 1668, 1667, 1670, + 1671, 1677, 1676, 1678, 1672, 1688, 1681, 1705, + 1711, 1715, 1713, 1722, 1723, 1728, 1729, 1726, + 1746, 1747, 1709, 1735, 1734, 1736, 1655, 1739, + 1733, 1737, 1744, 1609, 1574, 1575, 1574, 1749, + 1574, 1608, 1574, 1735, 1574, 1734, 1574, 1736, + 1574, 1744, 1574, 1609, 1740, 1574, 1580, 1574, + 1581, 1574, 1605, 1574, 1610, 1576, 1580, 1576, + 1581, 1576, 1582, 1576, 1605, 1576, 1609, 1576, + 1610, 1578, 1580, 1578, 1581, 1578, 1582, 1578, + 1605, 1578, 1609, 1578, 1610, 1579, 1580, 1579, + 1605, 1579, 1609, 1579, 1610, 1580, 1581, 1580, + 1605, 1581, 1580, 1581, 1605, 1582, 1580, 1582, + 1581, 1582, 1605, 1587, 1580, 1587, 1581, 1587, + 1582, 1587, 1605, 1589, 1581, 1589, 1605, 1590, + 1580, 1590, 1581, 1590, 1582, 1590, 1605, 1591, + 1581, 1591, 1605, 1592, 1605, 1593, 1580, 1593, + 1605, 1594, 1580, 1594, 1605, 1601, 1580, 1601, + 1581, 1601, 1582, 1601, 1605, 1601, 1609, 1601, + 1610, 1602, 1581, 1602, 1605, 1602, 1609, 1602, + 1610, 1603, 1575, 1603, 1580, 1603, 1581, 1603, + 1582, 1603, 1604, 1603, 1605, 1603, 1609, 1603, + 1610, 1604, 1580, 1604, 1581, 1604, 1582, 1604, + 1605, 1604, 1609, 1604, 1610, 1605, 1580, 1605, + 1581, 1605, 1582, 1605, 1605, 1605, 1609, 1605, + 1610, 1606, 1580, 1606, 1581, 1606, 1582, 1606, + 1605, 1606, 1609, 1606, 1610, 1607, 1580, 1607, + 1605, 1607, 1609, 1607, 1610, 1610, 1580, 1610, + 1581, 1610, 1582, 1610, 1605, 1610, 1609, 1610, + 1610, 1584, 1648, 1585, 1648, 1609, 1648, 32, + 1612, 1617, 32, 1613, 1617, 32, 1614, 1617, + 32, 1615, 1617, 32, 1616, 1617, 32, 1617, + 1648, 1574, 1585, 1574, 1586, 1574, 1606, 1576, + 1585, 1576, 1586, 1576, 1606, 1578, 1585, 1578, + 1586, 1578, 1606, 1579, 1585, 1579, 1586, 1579, + 1606, 1605, 1575, 1606, 1585, 1606, 1586, 1606, + 1606, 1610, 1585, 1610, 1586, 1610, 1606, 1574, + 1582, 1574, 1607, 1576, 1607, 1578, 1607, 1589, + 1582, 1604, 1607, 1606, 1607, 1607, 1648, 1610, + 1607, 1579, 1607, 1587, 1607, 1588, 1605, 1588, + 1607, 1600, 1614, 1617, 1600, 1615, 1617, 1600, + 1616, 1617, 1591, 1609, 1591, 1610, 1593, 1609, + 1593, 1610, 1594, 1609, 1594, 1610, 1587, 1609, + 1587, 1610, 1588, 1609, 1588, 1610, 1581, 1609, + 1581, 1610, 1580, 1609, 1580, 1610, 1582, 1609, + 1582, 1610, 1589, 1609, 1589, 1610, 1590, 1609, + 1590, 1610, 1588, 1580, 1588, 1581, 1588, 1582, + 1588, 1585, 1587, 1585, 1589, 1585, 1590, 1585, + 1575, 1611, 1578, 1580, 1605, 1578, 1581, 1580, + 1578, 1581, 1605, 1578, 1582, 1605, 1578, 1605, + 1580, 1578, 1605, 1581, 1578, 1605, 1582, 1580, + 1605, 1581, 1581, 1605, 1610, 1581, 1605, 1609, + 1587, 1581, 1580, 1587, 1580, 1581, 1587, 1580, + 1609, 1587, 1605, 1581, 1587, 1605, 1580, 1587, + 1605, 1605, 1589, 1581, 1581, 1589, 1605, 1605, + 1588, 1581, 1605, 1588, 1580, 1610, 1588, 1605, + 1582, 1588, 1605, 1605, 1590, 1581, 1609, 1590, + 1582, 1605, 1591, 1605, 1581, 1591, 1605, 1605, + 1591, 1605, 1610, 1593, 1580, 1605, 1593, 1605, + 1605, 1593, 1605, 1609, 1594, 1605, 1605, 1594, + 1605, 1610, 1594, 1605, 1609, 1601, 1582, 1605, + 1602, 1605, 1581, 1602, 1605, 1605, 1604, 1581, + 1605, 1604, 1581, 1610, 1604, 1581, 1609, 1604, + 1580, 1580, 1604, 1582, 1605, 1604, 1605, 1581, + 1605, 1581, 1580, 1605, 1581, 1605, 1605, 1581, + 1610, 1605, 1580, 1581, 1605, 1580, 1605, 1605, + 1582, 1580, 1605, 1582, 1605, 1605, 1580, 1582, + 1607, 1605, 1580, 1607, 1605, 1605, 1606, 1581, + 1605, 1606, 1581, 1609, 1606, 1580, 1605, 1606, + 1580, 1609, 1606, 1605, 1610, 1606, 1605, 1609, + 1610, 1605, 1605, 1576, 1582, 1610, 1578, 1580, + 1610, 1578, 1580, 1609, 1578, 1582, 1610, 1578, + 1582, 1609, 1578, 1605, 1610, 1578, 1605, 1609, + 1580, 1605, 1610, 1580, 1581, 1609, 1580, 1605, + 1609, 1587, 1582, 1609, 1589, 1581, 1610, 1588, + 1581, 1610, 1590, 1581, 1610, 1604, 1580, 1610, + 1604, 1605, 1610, 1610, 1581, 1610, 1610, 1580, + 1610, 1610, 1605, 1610, 1605, 1605, 1610, 1602, + 1605, 1610, 1606, 1581, 1610, 1593, 1605, 1610, + 1603, 1605, 1610, 1606, 1580, 1581, 1605, 1582, + 1610, 1604, 1580, 1605, 1603, 1605, 1605, 1580, + 1581, 1610, 1581, 1580, 1610, 1605, 1580, 1610, + 1601, 1605, 1610, 1576, 1581, 1610, 1587, 1582, + 1610, 1606, 1580, 1610, 1589, 1604, 1746, 1602, + 1604, 1746, 1575, 1604, 1604, 1607, 1575, 1603, + 1576, 1585, 1605, 1581, 1605, 1583, 1589, 1604, + 1593, 1605, 1585, 1587, 1608, 1604, 1593, 1604, + 1610, 1607, 1608, 1587, 1604, 1605, 1589, 1604, + 1609, 17, 1589, 1604, 1609, 32, 1575, 1604, + 1604, 1607, 32, 1593, 1604, 1610, 1607, 32, + 1608, 1587, 1604, 1605, 7, 1580, 1604, 32, + 1580, 1604, 1575, 1604, 1607, 1585, 1740, 1575, + 1604, 44, 12289, 12290, 58, 33, 63, 12310, + 12311, 8230, 8229, 8212, 8211, 95, 123, 125, + 12308, 12309, 12304, 12305, 12298, 12299, 12300, 12301, + 12302, 12303, 91, 93, 8254, 35, 38, 42, + 45, 60, 62, 92, 36, 37, 64, 32, + 1611, 1600, 1611, 32, 1612, 32, 1613, 32, + 1614, 1600, 1614, 32, 1615, 1600, 1615, 32, + 1616, 1600, 1616, 32, 1617, 1600, 1617, 32, + 1618, 1600, 1618, 1569, 1570, 1571, 1572, 1573, + 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, + 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, + 1590, 1591, 1592, 1593, 1594, 1601, 1602, 1603, + 1604, 1605, 1606, 1607, 1608, 1610, 1604, 1570, + 1604, 1571, 1604, 1573, 1604, 1575, 34, 39, + 47, 65345, 65346, 65347, 65348, 65349, 65350, 65351, + 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, + 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, + 65368, 65369, 65370, 94, 124, 126, 10629, 10630, + 12539, 12449, 12451, 12453, 12455, 12457, 12515, 12517, + 12519, 12483, 12540, 12531, 12441, 12442, 12644, 12593, + 12594, 12595, 12596, 12597, 12598, 12599, 12600, 12601, + 12602, 12603, 12604, 12605, 12606, 12607, 12608, 12609, + 12610, 12611, 12612, 12613, 12614, 12615, 12616, 12617, + 12618, 12619, 12620, 12621, 12622, 12623, 12624, 12625, + 12626, 12627, 12628, 12629, 12630, 12631, 12632, 12633, + 12634, 12635, 12636, 12637, 12638, 12639, 12640, 12641, + 12642, 12643, 162, 163, 172, 175, 166, 165, + 8361, 9474, 8592, 8593, 8594, 8595, 9632, 9675, + 55297, 56360, 55297, 56361, 55297, 56362, 55297, 56363, + 55297, 56364, 55297, 56365, 55297, 56366, 55297, 56367, + 55297, 56368, 55297, 56369, 55297, 56370, 55297, 56371, + 55297, 56372, 55297, 56373, 55297, 56374, 55297, 56375, + 55297, 56376, 55297, 56377, 55297, 56378, 55297, 56379, + 55297, 56380, 55297, 56381, 55297, 56382, 55297, 56383, + 55297, 56384, 55297, 56385, 55297, 56386, 55297, 56387, + 55297, 56388, 55297, 56389, 55297, 56390, 55297, 56391, + 55297, 56392, 55297, 56393, 55297, 56394, 55297, 56395, + 55297, 56396, 55297, 56397, 55297, 56398, 55297, 56399, + 55297, 56536, 55297, 56537, 55297, 56538, 55297, 56539, + 55297, 56540, 55297, 56541, 55297, 56542, 55297, 56543, + 55297, 56544, 55297, 56545, 55297, 56546, 55297, 56547, + 55297, 56548, 55297, 56549, 55297, 56550, 55297, 56551, + 55297, 56552, 55297, 56553, 55297, 56554, 55297, 56555, + 55297, 56556, 55297, 56557, 55297, 56558, 55297, 56559, + 55297, 56560, 55297, 56561, 55297, 56562, 55297, 56563, + 55297, 56564, 55297, 56565, 55297, 56566, 55297, 56567, + 55297, 56568, 55297, 56569, 55297, 56570, 55297, 56571, + 55299, 56512, 55299, 56513, 55299, 56514, 55299, 56515, + 55299, 56516, 55299, 56517, 55299, 56518, 55299, 56519, + 55299, 56520, 55299, 56521, 55299, 56522, 55299, 56523, + 55299, 56524, 55299, 56525, 55299, 56526, 55299, 56527, + 55299, 56528, 55299, 56529, 55299, 56530, 55299, 56531, + 55299, 56532, 55299, 56533, 55299, 56534, 55299, 56535, + 55299, 56536, 55299, 56537, 55299, 56538, 55299, 56539, + 55299, 56540, 55299, 56541, 55299, 56542, 55299, 56543, + 55299, 56544, 55299, 56545, 55299, 56546, 55299, 56547, + 55299, 56548, 55299, 56549, 55299, 56550, 55299, 56551, + 55299, 56552, 55299, 56553, 55299, 56554, 55299, 56555, + 55299, 56556, 55299, 56557, 55299, 56558, 55299, 56559, + 55299, 56560, 55299, 56561, 55299, 56562, 55300, 56473, + 55300, 56506, 55300, 56475, 55300, 56506, 55300, 56485, + 55300, 56506, 55300, 56625, 55300, 56615, 55300, 56626, + 55300, 56615, 55300, 57159, 55300, 57150, 55300, 57159, + 55300, 57175, 55301, 56505, 55301, 56506, 55301, 56505, + 55301, 56496, 55301, 56505, 55301, 56509, 55301, 56760, + 55301, 56751, 55301, 56761, 55301, 56751, 55302, 56512, + 55302, 56513, 55302, 56514, 55302, 56515, 55302, 56516, + 55302, 56517, 55302, 56518, 55302, 56519, 55302, 56520, + 55302, 56521, 55302, 56522, 55302, 56523, 55302, 56524, + 55302, 56525, 55302, 56526, 55302, 56527, 55302, 56528, + 55302, 56529, 55302, 56530, 55302, 56531, 55302, 56532, + 55302, 56533, 55302, 56534, 55302, 56535, 55302, 56536, + 55302, 56537, 55302, 56538, 55302, 56539, 55302, 56540, + 55302, 56541, 55302, 56542, 55302, 56543, 55323, 56928, + 55323, 56929, 55323, 56930, 55323, 56931, 55323, 56932, + 55323, 56933, 55323, 56934, 55323, 56935, 55323, 56936, + 55323, 56937, 55323, 56938, 55323, 56939, 55323, 56940, + 55323, 56941, 55323, 56942, 55323, 56943, 55323, 56944, + 55323, 56945, 55323, 56946, 55323, 56947, 55323, 56948, + 55323, 56949, 55323, 56950, 55323, 56951, 55323, 56952, + 55323, 56953, 55323, 56954, 55323, 56955, 55323, 56956, + 55323, 56957, 55323, 56958, 55323, 56959, 55348, 56663, + 55348, 56677, 55348, 56664, 55348, 56677, 55348, 56671, + 55348, 56686, 55348, 56671, 55348, 56687, 55348, 56671, + 55348, 56688, 55348, 56671, 55348, 56689, 55348, 56671, + 55348, 56690, 55348, 56761, 55348, 56677, 55348, 56762, + 55348, 56677, 55348, 56763, 55348, 56686, 55348, 56764, + 55348, 56686, 55348, 56763, 55348, 56687, 55348, 56764, + 55348, 56687, 305, 567, 913, 914, 916, 917, + 918, 919, 921, 922, 923, 924, 925, 926, + 927, 929, 1012, 932, 934, 935, 936, 8711, + 8706, 1013, 977, 1008, 981, 1009, 982, 988, + 55354, 56610, 55354, 56611, 55354, 56612, 55354, 56613, + 55354, 56614, 55354, 56615, 55354, 56616, 55354, 56617, + 55354, 56618, 55354, 56619, 55354, 56620, 55354, 56621, + 55354, 56622, 55354, 56623, 55354, 56624, 55354, 56625, + 55354, 56626, 55354, 56627, 55354, 56628, 55354, 56629, + 55354, 56630, 55354, 56631, 55354, 56632, 55354, 56633, + 55354, 56634, 55354, 56635, 55354, 56636, 55354, 56637, + 55354, 56638, 55354, 56639, 55354, 56640, 55354, 56641, + 55354, 56642, 55354, 56643, 1646, 1697, 1647, 48, + 46, 48, 44, 49, 44, 50, 44, 51, + 44, 52, 44, 53, 44, 54, 44, 55, + 44, 56, 44, 57, 44, 40, 65, 41, + 40, 66, 41, 40, 67, 41, 40, 68, + 41, 40, 69, 41, 40, 70, 41, 40, + 71, 41, 40, 72, 41, 40, 73, 41, + 40, 74, 41, 40, 75, 41, 40, 76, + 41, 40, 77, 41, 40, 78, 41, 40, + 79, 41, 40, 80, 41, 40, 81, 41, + 40, 82, 41, 40, 83, 41, 40, 84, + 41, 40, 85, 41, 40, 86, 41, 40, + 87, 41, 40, 88, 41, 40, 89, 41, + 40, 90, 41, 12308, 83, 12309, 67, 68, + 87, 90, 72, 86, 83, 68, 83, 83, + 80, 80, 86, 87, 67, 77, 67, 77, + 68, 77, 82, 68, 74, 12411, 12363, 12467, + 12467, 23383, 21452, 12487, 22810, 35299, 20132, 26144, + 28961, 21069, 24460, 20877, 26032, 21021, 32066, 36009, + 22768, 21561, 28436, 25237, 25429, 36938, 25351, 25171, + 31105, 31354, 21512, 28288, 30003, 21106, 21942, 37197, + 12308, 26412, 12309, 12308, 19977, 12309, 12308, 20108, + 12309, 12308, 23433, 12309, 12308, 28857, 12309, 12308, + 25171, 12309, 12308, 30423, 12309, 12308, 21213, 12309, + 12308, 25943, 12309, 24471, 21487, 20029, 20024, 20033, + 55360, 56610, 20320, 20411, 20482, 20602, 20633, 20687, + 13470, 55361, 56890, 20820, 20836, 20855, 55361, 56604, + 13497, 20839, 55361, 56651, 20887, 20900, 20172, 20908, + 55396, 56799, 20995, 13535, 21051, 21062, 21111, 13589, + 21253, 21254, 21321, 21338, 21363, 21373, 21375, 55362, + 56876, 28784, 21450, 21471, 55362, 57187, 21483, 21489, + 21510, 21662, 21560, 21576, 21608, 21666, 21750, 21776, + 21843, 21859, 21892, 21931, 21939, 21954, 22294, 22295, + 22097, 22132, 22766, 22478, 22516, 22541, 22411, 22578, + 22577, 22700, 55365, 56548, 22770, 22775, 22790, 22818, + 22882, 55365, 57000, 55365, 57066, 23020, 23067, 23079, + 23000, 23142, 14062, 14076, 23304, 23358, 55366, 56776, + 23491, 23512, 23539, 55366, 57112, 23551, 23558, 24403, + 14209, 23648, 23744, 23693, 55367, 56804, 23875, 55367, + 56806, 23918, 23915, 23932, 24033, 24034, 14383, 24061, + 24104, 24125, 24169, 14434, 55368, 56707, 14460, 24240, + 24243, 24246, 55400, 57234, 55368, 57137, 33281, 24354, + 14535, 55372, 57016, 55384, 56794, 24418, 24427, 14563, + 24474, 24525, 24535, 24569, 24705, 14650, 14620, 55369, + 57044, 24775, 24904, 24908, 24954, 25010, 24996, 25007, + 25054, 25104, 25115, 25181, 25265, 25300, 25424, 55370, + 57100, 25405, 25340, 25448, 25475, 25572, 55370, 57329, + 25634, 25541, 25513, 14894, 25705, 25726, 25757, 25719, + 14956, 25964, 55372, 56330, 26083, 26360, 26185, 15129, + 15112, 15076, 20882, 20885, 26368, 26268, 32941, 17369, + 26401, 26462, 26451, 55372, 57283, 15177, 26618, 26501, + 26706, 55373, 56429, 26766, 26655, 26900, 26946, 27043, + 27114, 27304, 55373, 56995, 27355, 15384, 27425, 55374, + 56487, 27476, 15438, 27506, 27551, 27579, 55374, 56973, + 55367, 56587, 55374, 57082, 27726, 55375, 56508, 27839, + 27853, 27751, 27926, 27966, 28009, 28024, 28037, 55375, + 56606, 27956, 28207, 28270, 15667, 28359, 55375, 57041, + 28153, 28526, 55375, 57182, 55375, 57230, 28614, 28729, + 28699, 15766, 28746, 28797, 28791, 28845, 55361, 56613, + 28997, 55376, 56931, 29084, 55376, 57259, 29224, 29264, + 55377, 56840, 29312, 29333, 55377, 57141, 55378, 56340, + 29562, 29579, 16044, 29605, 16056, 29767, 29788, 29829, + 29898, 16155, 29988, 55379, 56374, 30014, 55379, 56466, + 55368, 56735, 30224, 55379, 57249, 55379, 57272, 55380, + 56388, 16380, 16392, 55380, 56563, 55380, 56562, 55380, + 56601, 55380, 56627, 30494, 30495, 30603, 16454, 16534, + 55381, 56349, 30798, 16611, 55381, 56870, 55381, 56986, + 55381, 57029, 31211, 16687, 31306, 31311, 55382, 56700, + 55382, 56999, 31470, 16898, 55382, 57259, 31686, 31689, + 16935, 55383, 56448, 31954, 17056, 31976, 31971, 32000, + 55383, 57222, 32099, 17153, 32199, 32258, 32325, 17204, + 55384, 56872, 55384, 56903, 17241, 55384, 57049, 32634, + 55384, 57150, 32661, 32762, 55385, 56538, 55385, 56611, + 32864, 55385, 56744, 32880, 55372, 57183, 17365, 32946, + 33027, 17419, 33086, 23221, 55385, 57255, 55385, 57269, + 55372, 57235, 55372, 57244, 33284, 36766, 17515, 33425, + 33419, 33437, 21171, 33457, 33459, 33469, 33510, 55386, + 57148, 33565, 33635, 33709, 33571, 33725, 33767, 33619, + 33738, 33740, 33756, 55387, 56374, 55387, 56683, 55387, + 56533, 17707, 34033, 34035, 34070, 55388, 57290, 34148, + 55387, 57132, 17757, 17761, 55387, 57265, 55388, 56530, + 17771, 34384, 34407, 34409, 34473, 34440, 34574, 34530, + 34600, 34667, 34694, 17879, 34785, 34817, 17913, 34912, + 55389, 56935, 35031, 35038, 17973, 35066, 13499, 55390, + 56494, 55390, 56678, 18110, 18119, 35488, 55391, 56488, + 36011, 36033, 36123, 36215, 55391, 57135, 55362, 56324, + 36299, 36284, 36336, 55362, 56542, 36564, 55393, 56786, + 55393, 56813, 37012, 37105, 37137, 55393, 57134, 37147, + 37432, 37591, 37592, 37500, 37881, 37909, 55394, 57338, + 38283, 18837, 38327, 55395, 56695, 18918, 38595, 23986, + 38691, 55396, 56645, 55396, 56858, 19054, 19062, 38880, + 55397, 56330, 19122, 55397, 56470, 38953, 55397, 56758, + 39138, 19251, 39209, 39335, 39362, 39422, 19406, 55398, + 57136, 40000, 40189, 19662, 19693, 40295, 55400, 56526, + 19704, 55400, 56581, 55400, 56846, 55400, 56977, 19798, + 40702, 40709, 40719, 40726, 55401, 56832, 7838, 192, + 193, 194, 195, 196, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 216, 217, 218, 219, + 220, 221, 222, 376, 256, 258, 260, 262, + 264, 266, 268, 270, 272, 274, 276, 278, + 280, 282, 284, 286, 288, 290, 292, 296, + 298, 300, 302, 306, 308, 310, 313, 315, + 317, 319, 321, 323, 325, 327, 330, 332, + 334, 336, 338, 340, 342, 344, 346, 348, + 350, 352, 354, 356, 358, 360, 362, 364, + 366, 368, 370, 372, 374, 377, 379, 381, + 579, 386, 388, 391, 395, 401, 502, 408, + 573, 544, 416, 418, 420, 423, 428, 431, + 435, 437, 440, 444, 503, 453, 452, 456, + 455, 459, 458, 461, 463, 465, 467, 469, + 471, 473, 475, 478, 480, 482, 484, 486, + 488, 490, 492, 494, 498, 497, 500, 504, + 506, 508, 510, 512, 514, 516, 518, 520, + 522, 524, 526, 528, 530, 532, 534, 536, + 538, 540, 542, 548, 550, 552, 554, 556, + 558, 560, 562, 571, 11390, 11391, 577, 582, + 584, 586, 588, 590, 11375, 11373, 11376, 385, + 390, 393, 394, 399, 42923, 403, 42924, 404, + 42893, 42922, 407, 406, 42926, 11362, 42925, 412, + 11374, 413, 415, 11364, 422, 42949, 425, 42929, + 430, 580, 433, 434, 581, 439, 42930, 42928, + 880, 882, 886, 1021, 1022, 1023, 938, 939, + 975, 984, 986, 990, 992, 994, 996, 998, + 1000, 1002, 1004, 1006, 1017, 895, 1015, 1018, + 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, + 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, + 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, + 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, + 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, + 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, + 1120, 1122, 1124, 1126, 1128, 1130, 1132, 1134, + 1136, 1138, 1140, 1142, 1144, 1146, 1148, 1150, + 1152, 1162, 1164, 1166, 1168, 1170, 1172, 1174, + 1176, 1178, 1180, 1182, 1184, 1186, 1188, 1190, + 1192, 1194, 1196, 1198, 1200, 1202, 1204, 1206, + 1208, 1210, 1212, 1214, 1217, 1219, 1221, 1223, + 1225, 1227, 1229, 1216, 1232, 1234, 1236, 1238, + 1240, 1242, 1244, 1246, 1248, 1250, 1252, 1254, + 1256, 1258, 1260, 1262, 1264, 1266, 1268, 1270, + 1272, 1274, 1276, 1278, 1280, 1282, 1284, 1286, + 1288, 1290, 1292, 1294, 1296, 1298, 1300, 1302, + 1304, 1306, 1308, 1310, 1312, 1314, 1316, 1318, + 1320, 1322, 1324, 1326, 1329, 1330, 1331, 1332, + 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, + 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, + 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, + 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, + 1365, 1366, 7312, 7313, 7314, 7315, 7316, 7317, + 7318, 7319, 7320, 7321, 7322, 7323, 7324, 7325, + 7326, 7327, 7328, 7329, 7330, 7331, 7332, 7333, + 7334, 7335, 7336, 7337, 7338, 7339, 7340, 7341, + 7342, 7343, 7344, 7345, 7346, 7347, 7348, 7349, + 7350, 7351, 7352, 7353, 7354, 7357, 7358, 7359, + 43888, 43889, 43890, 43891, 43892, 43893, 43894, 43895, + 43896, 43897, 43898, 43899, 43900, 43901, 43902, 43903, + 43904, 43905, 43906, 43907, 43908, 43909, 43910, 43911, + 43912, 43913, 43914, 43915, 43916, 43917, 43918, 43919, + 43920, 43921, 43922, 43923, 43924, 43925, 43926, 43927, + 43928, 43929, 43930, 43931, 43932, 43933, 43934, 43935, + 43936, 43937, 43938, 43939, 43940, 43941, 43942, 43943, + 43944, 43945, 43946, 43947, 43948, 43949, 43950, 43951, + 43952, 43953, 43954, 43955, 43956, 43957, 43958, 43959, + 43960, 43961, 43962, 43963, 43964, 43965, 43966, 43967, + 5112, 5113, 5114, 5115, 5116, 5117, 42570, 42877, + 11363, 42950, 7680, 7682, 7684, 7686, 7688, 7690, + 7692, 7694, 7696, 7698, 7700, 7702, 7704, 7706, + 7708, 7710, 7712, 7714, 7716, 7718, 7720, 7722, + 7724, 7726, 7728, 7730, 7732, 7734, 7736, 7738, + 7740, 7742, 7744, 7746, 7748, 7750, 7752, 7754, + 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, + 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786, + 7788, 7790, 7792, 7794, 7796, 7798, 7800, 7802, + 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, + 7820, 7822, 7824, 7826, 7828, 223, 7840, 7842, + 7844, 7846, 7848, 7850, 7852, 7854, 7856, 7858, + 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, + 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, + 7892, 7894, 7896, 7898, 7900, 7902, 7904, 7906, + 7908, 7910, 7912, 7914, 7916, 7918, 7920, 7922, + 7924, 7926, 7928, 7930, 7932, 7934, 7944, 7945, + 7946, 7947, 7948, 7949, 7950, 7951, 7960, 7961, + 7962, 7963, 7964, 7965, 7976, 7977, 7978, 7979, + 7980, 7981, 7982, 7983, 7992, 7993, 7994, 7995, + 7996, 7997, 7998, 7999, 8008, 8009, 8010, 8011, + 8012, 8013, 8025, 8027, 8029, 8031, 8040, 8041, + 8042, 8043, 8044, 8045, 8046, 8047, 8122, 8123, + 8136, 8137, 8138, 8139, 8154, 8155, 8184, 8185, + 8170, 8171, 8186, 8187, 8072, 8073, 8074, 8075, + 8076, 8077, 8078, 8079, 8064, 8065, 8066, 8067, + 8068, 8069, 8070, 8071, 8088, 8089, 8090, 8091, + 8092, 8093, 8094, 8095, 8080, 8081, 8082, 8083, + 8084, 8085, 8086, 8087, 8104, 8105, 8106, 8107, + 8108, 8109, 8110, 8111, 8096, 8097, 8098, 8099, + 8100, 8101, 8102, 8103, 8120, 8121, 8124, 8115, + 8140, 8131, 8152, 8153, 8168, 8169, 8172, 8188, + 8179, 8498, 8544, 8545, 8546, 8547, 8548, 8549, + 8550, 8551, 8552, 8553, 8554, 8555, 8556, 8557, + 8558, 8559, 8579, 9398, 9399, 9400, 9401, 9402, + 9403, 9404, 9405, 9406, 9407, 9408, 9409, 9410, + 9411, 9412, 9413, 9414, 9415, 9416, 9417, 9418, + 9419, 9420, 9421, 9422, 9423, 11264, 11265, 11266, + 11267, 11268, 11269, 11270, 11271, 11272, 11273, 11274, + 11275, 11276, 11277, 11278, 11279, 11280, 11281, 11282, + 11283, 11284, 11285, 11286, 11287, 11288, 11289, 11290, + 11291, 11292, 11293, 11294, 11295, 11296, 11297, 11298, + 11299, 11300, 11301, 11302, 11303, 11304, 11305, 11306, + 11307, 11308, 11309, 11310, 11360, 570, 574, 11367, + 11369, 11371, 11378, 11381, 11392, 11394, 11396, 11398, + 11400, 11402, 11404, 11406, 11408, 11410, 11412, 11414, + 11416, 11418, 11420, 11422, 11424, 11426, 11428, 11430, + 11432, 11434, 11436, 11438, 11440, 11442, 11444, 11446, + 11448, 11450, 11452, 11454, 11456, 11458, 11460, 11462, + 11464, 11466, 11468, 11470, 11472, 11474, 11476, 11478, + 11480, 11482, 11484, 11486, 11488, 11490, 11499, 11501, + 11506, 4256, 4257, 4258, 4259, 4260, 4261, 4262, + 4263, 4264, 4265, 4266, 4267, 4268, 4269, 4270, + 4271, 4272, 4273, 4274, 4275, 4276, 4277, 4278, + 4279, 4280, 4281, 4282, 4283, 4284, 4285, 4286, + 4287, 4288, 4289, 4290, 4291, 4292, 4293, 4295, + 4301, 42560, 42562, 42564, 42566, 42568, 42572, 42574, + 42576, 42578, 42580, 42582, 42584, 42586, 42588, 42590, + 42592, 42594, 42596, 42598, 42600, 42602, 42604, 42624, + 42626, 42628, 42630, 42632, 42634, 42636, 42638, 42640, + 42642, 42644, 42646, 42648, 42650, 42786, 42788, 42790, + 42792, 42794, 42796, 42798, 42802, 42804, 42806, 42808, + 42810, 42812, 42814, 42816, 42818, 42820, 42822, 42824, + 42826, 42828, 42830, 42832, 42834, 42836, 42838, 42840, + 42842, 42844, 42846, 42848, 42850, 42852, 42854, 42856, + 42858, 42860, 42862, 42873, 42875, 42878, 42880, 42882, + 42884, 42886, 42891, 42896, 42898, 42948, 42902, 42904, + 42906, 42908, 42910, 42912, 42914, 42916, 42918, 42920, + 42932, 42934, 42936, 42938, 42940, 42942, 42946, 42931, + 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, + 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, + 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, + 65337, 65338, 55297, 56320, 55297, 56321, 55297, 56322, + 55297, 56323, 55297, 56324, 55297, 56325, 55297, 56326, + 55297, 56327, 55297, 56328, 55297, 56329, 55297, 56330, + 55297, 56331, 55297, 56332, 55297, 56333, 55297, 56334, + 55297, 56335, 55297, 56336, 55297, 56337, 55297, 56338, + 55297, 56339, 55297, 56340, 55297, 56341, 55297, 56342, + 55297, 56343, 55297, 56344, 55297, 56345, 55297, 56346, + 55297, 56347, 55297, 56348, 55297, 56349, 55297, 56350, + 55297, 56351, 55297, 56352, 55297, 56353, 55297, 56354, + 55297, 56355, 55297, 56356, 55297, 56357, 55297, 56358, + 55297, 56359, 55297, 56496, 55297, 56497, 55297, 56498, + 55297, 56499, 55297, 56500, 55297, 56501, 55297, 56502, + 55297, 56503, 55297, 56504, 55297, 56505, 55297, 56506, + 55297, 56507, 55297, 56508, 55297, 56509, 55297, 56510, + 55297, 56511, 55297, 56512, 55297, 56513, 55297, 56514, + 55297, 56515, 55297, 56516, 55297, 56517, 55297, 56518, + 55297, 56519, 55297, 56520, 55297, 56521, 55297, 56522, + 55297, 56523, 55297, 56524, 55297, 56525, 55297, 56526, + 55297, 56527, 55297, 56528, 55297, 56529, 55297, 56530, + 55297, 56531, 55299, 56448, 55299, 56449, 55299, 56450, + 55299, 56451, 55299, 56452, 55299, 56453, 55299, 56454, + 55299, 56455, 55299, 56456, 55299, 56457, 55299, 56458, + 55299, 56459, 55299, 56460, 55299, 56461, 55299, 56462, + 55299, 56463, 55299, 56464, 55299, 56465, 55299, 56466, + 55299, 56467, 55299, 56468, 55299, 56469, 55299, 56470, + 55299, 56471, 55299, 56472, 55299, 56473, 55299, 56474, + 55299, 56475, 55299, 56476, 55299, 56477, 55299, 56478, + 55299, 56479, 55299, 56480, 55299, 56481, 55299, 56482, + 55299, 56483, 55299, 56484, 55299, 56485, 55299, 56486, + 55299, 56487, 55299, 56488, 55299, 56489, 55299, 56490, + 55299, 56491, 55299, 56492, 55299, 56493, 55299, 56494, + 55299, 56495, 55299, 56496, 55299, 56497, 55299, 56498, + 55302, 56480, 55302, 56481, 55302, 56482, 55302, 56483, + 55302, 56484, 55302, 56485, 55302, 56486, 55302, 56487, + 55302, 56488, 55302, 56489, 55302, 56490, 55302, 56491, + 55302, 56492, 55302, 56493, 55302, 56494, 55302, 56495, + 55302, 56496, 55302, 56497, 55302, 56498, 55302, 56499, + 55302, 56500, 55302, 56501, 55302, 56502, 55302, 56503, + 55302, 56504, 55302, 56505, 55302, 56506, 55302, 56507, + 55302, 56508, 55302, 56509, 55302, 56510, 55302, 56511, + 55323, 56896, 55323, 56897, 55323, 56898, 55323, 56899, + 55323, 56900, 55323, 56901, 55323, 56902, 55323, 56903, + 55323, 56904, 55323, 56905, 55323, 56906, 55323, 56907, + 55323, 56908, 55323, 56909, 55323, 56910, 55323, 56911, + 55323, 56912, 55323, 56913, 55323, 56914, 55323, 56915, + 55323, 56916, 55323, 56917, 55323, 56918, 55323, 56919, + 55323, 56920, 55323, 56921, 55323, 56922, 55323, 56923, + 55323, 56924, 55323, 56925, 55323, 56926, 55323, 56927, + 55354, 56576, 55354, 56577, 55354, 56578, 55354, 56579, + 55354, 56580, 55354, 56581, 55354, 56582, 55354, 56583, + 55354, 56584, 55354, 56585, 55354, 56586, 55354, 56587, + 55354, 56588, 55354, 56589, 55354, 56590, 55354, 56591, + 55354, 56592, 55354, 56593, 55354, 56594, 55354, 56595, + 55354, 56596, 55354, 56597, 55354, 56598, 55354, 56599, + 55354, 56600, 55354, 56601, 55354, 56602, 55354, 56603, + 55354, 56604, 55354, 56605, 55354, 56606, 55354, 56607, + 55354, 56608, 55354, 56609, }; + +static const utf8proc_uint16_t utf8proc_stage1table[] = { + 0, 256, 512, 768, 1024, 1280, 1536, + 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, + 3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632, + 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680, + 7936, 8192, 8448, 8704, 8960, 9216, 9472, 9728, + 9984, 10240, 10496, 10752, 11008, 11264, 11520, 11776, + 12032, 12288, 12544, 12800, 13056, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13568, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13824, 14080, 13312, 13312, 13312, 14336, 5376, 14592, + 14848, 15104, 15360, 15616, 15872, 16128, 16384, 16640, + 16896, 17152, 17408, 17664, 16128, 16384, 16640, 16896, + 17152, 17408, 17664, 16128, 16384, 16640, 16896, 17152, + 17408, 17664, 16128, 16384, 16640, 16896, 17152, 17408, + 17664, 16128, 16384, 16640, 16896, 17152, 17408, 17664, + 16128, 16384, 16640, 16896, 17152, 17408, 17664, 16128, + 17920, 18176, 18176, 18176, 18176, 18176, 18176, 18176, + 18176, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18688, 18944, 19200, 19456, 19712, 19968, + 20224, 20480, 20736, 20992, 21248, 21504, 21760, 5376, + 22016, 22272, 22528, 22784, 23040, 23296, 23552, 23808, + 24064, 24320, 24576, 24832, 25088, 25344, 25600, 25856, + 26112, 26368, 26624, 26880, 27136, 27392, 27648, 27904, + 28160, 5376, 5376, 5376, 28416, 28672, 28928, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 5376, 5376, 5376, 5376, 29184, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 5376, 5376, 29440, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 5376, 5376, 29696, 29952, 27136, 27136, 30208, + 30464, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 30720, 13312, 13312, 30976, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 13312, 31232, 31488, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 31744, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 32000, 32256, 32512, 32768, 33024, 33280, 33536, + 33792, 10240, 10240, 34048, 27136, 27136, 27136, 27136, + 27136, 34304, 34560, 34816, 27136, 27136, 27136, 27136, + 27136, 35072, 35328, 27136, 27136, 35584, 35840, 36096, + 27136, 36352, 36608, 36864, 37120, 37376, 37632, 37888, + 38144, 38400, 38656, 38912, 27136, 27136, 27136, 27136, + 27136, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 39168, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 39424, 39680, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 39936, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 13312, 13312, 13312, 13312, + 13312, 13312, 13312, 13312, 40192, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 40448, 40704, 40960, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 41216, 41472, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 27136, 27136, 27136, 27136, 27136, 27136, 27136, + 27136, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 41728, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, + 41728, }; + +static const utf8proc_uint16_t utf8proc_stage2table[] = { + 1, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 4, 3, 5, 6, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 7, 7, 7, + 3, 8, 9, 9, 10, 11, 10, 9, + 9, 12, 13, 9, 14, 15, 16, 15, + 15, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 15, 9, 18, 19, 20, + 9, 9, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 12, 9, 13, 47, + 48, 47, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 12, 75, 13, 75, + 2, 2, 2, 2, 2, 2, 7, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 76, 9, 11, 11, 11, 11, 77, + 9, 78, 79, 80, 81, 75, 82, 79, + 83, 84, 85, 86, 87, 88, 89, 9, + 9, 90, 91, 92, 93, 94, 95, 96, + 9, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 75, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, + 75, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 215, 300, + 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 215, 313, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 215, 215, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 340, 215, 341, 342, 343, 215, + 344, 341, 341, 341, 341, 345, 346, 347, + 348, 349, 350, 351, 352, 353, 354, 355, + 356, 357, 358, 359, 360, 361, 362, 363, + 364, 365, 366, 367, 368, 369, 370, 371, + 372, 373, 374, 375, 376, 377, 378, 379, + 380, 381, 382, 383, 384, 385, 386, 387, + 388, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, 415, 416, 417, 418, 419, + 420, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 430, 431, 432, 433, 434, 435, + 436, 437, 215, 438, 439, 440, 441, 442, + 443, 444, 445, 446, 447, 448, 449, 450, + 451, 452, 453, 454, 455, 215, 215, 215, + 215, 215, 215, 456, 457, 458, 459, 460, + 461, 462, 463, 464, 465, 466, 467, 468, + 469, 470, 471, 472, 473, 474, 475, 476, + 477, 478, 479, 480, 481, 482, 215, 483, + 484, 215, 485, 215, 486, 487, 215, 215, + 215, 488, 489, 215, 490, 215, 491, 492, + 215, 493, 494, 495, 496, 497, 215, 215, + 498, 215, 499, 500, 215, 215, 501, 215, + 215, 215, 215, 215, 215, 215, 502, 215, + 215, 503, 215, 504, 505, 215, 215, 215, + 506, 507, 508, 509, 510, 511, 215, 215, + 215, 215, 215, 512, 215, 341, 215, 215, + 215, 215, 215, 215, 215, 215, 513, 514, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 515, 516, 517, 518, 519, 520, 521, + 522, 523, 524, 524, 525, 525, 525, 525, + 525, 525, 525, 526, 526, 526, 526, 524, + 524, 524, 524, 524, 524, 524, 524, 524, + 524, 525, 525, 526, 526, 526, 526, 526, + 526, 527, 528, 529, 530, 531, 532, 526, + 526, 533, 534, 535, 536, 537, 526, 526, + 526, 526, 526, 526, 526, 524, 526, 525, + 526, 526, 526, 526, 526, 526, 526, 526, + 526, 526, 526, 526, 526, 526, 526, 526, + 526, 538, 539, 540, 541, 542, 543, 544, + 545, 546, 547, 548, 549, 550, 543, 543, + 551, 543, 552, 543, 553, 554, 555, 556, + 556, 556, 556, 555, 557, 556, 556, 556, + 556, 556, 558, 558, 559, 560, 561, 562, + 563, 564, 556, 556, 556, 556, 565, 566, + 556, 567, 568, 556, 556, 569, 569, 569, + 569, 570, 556, 556, 556, 556, 543, 543, + 543, 571, 572, 573, 574, 575, 576, 543, + 556, 556, 556, 543, 543, 543, 556, 556, + 577, 543, 543, 543, 556, 556, 556, 556, + 543, 555, 556, 556, 543, 578, 579, 579, + 578, 579, 579, 578, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 580, 581, 582, 583, 584, 526, 585, + 586, 0, 0, 587, 588, 589, 590, 591, + 592, 0, 0, 0, 0, 88, 593, 594, + 595, 596, 597, 598, 0, 599, 0, 600, + 601, 602, 603, 604, 605, 606, 607, 608, + 609, 610, 611, 612, 613, 614, 615, 616, + 617, 618, 619, 0, 620, 621, 622, 623, + 624, 625, 626, 627, 628, 629, 630, 631, + 632, 633, 634, 635, 636, 637, 638, 639, + 640, 641, 642, 643, 644, 645, 646, 647, + 648, 649, 650, 651, 652, 653, 654, 655, + 656, 657, 658, 659, 660, 661, 662, 663, + 664, 665, 666, 667, 668, 669, 670, 671, + 672, 673, 674, 675, 676, 677, 678, 679, + 680, 681, 682, 683, 684, 685, 686, 687, + 688, 689, 690, 691, 692, 693, 694, 695, + 696, 697, 698, 699, 700, 701, 702, 75, + 703, 704, 705, 706, 707, 215, 708, 709, + 710, 711, 712, 713, 714, 715, 716, 717, + 718, 719, 720, 721, 722, 723, 724, 725, + 726, 727, 728, 729, 730, 731, 732, 733, + 734, 735, 736, 737, 738, 739, 740, 741, + 742, 743, 744, 745, 746, 747, 748, 749, + 750, 751, 752, 753, 754, 755, 756, 757, + 758, 759, 760, 761, 762, 763, 764, 765, + 766, 767, 768, 769, 770, 771, 772, 773, + 774, 775, 776, 777, 778, 779, 780, 781, + 782, 783, 784, 785, 786, 787, 788, 789, + 790, 791, 792, 793, 794, 795, 796, 797, + 798, 799, 800, 801, 802, 803, 804, 805, + 806, 807, 808, 809, 810, 811, 812, 813, + 814, 815, 816, 817, 818, 819, 820, 821, + 822, 823, 824, 825, 826, 827, 828, 829, + 830, 831, 832, 833, 834, 835, 836, 837, + 838, 839, 840, 841, 543, 543, 543, 543, + 543, 842, 842, 843, 844, 845, 846, 847, + 848, 849, 850, 851, 852, 853, 854, 855, + 856, 857, 858, 859, 860, 861, 862, 863, + 864, 865, 866, 867, 868, 869, 870, 871, + 872, 873, 874, 875, 876, 877, 878, 879, + 880, 881, 882, 883, 884, 885, 886, 887, + 888, 889, 890, 891, 892, 893, 894, 895, + 896, 897, 898, 899, 900, 901, 902, 903, + 904, 905, 906, 907, 908, 909, 910, 911, + 912, 913, 914, 915, 916, 917, 918, 919, + 920, 921, 922, 923, 924, 925, 926, 927, + 928, 929, 930, 931, 932, 933, 934, 935, + 936, 937, 938, 939, 940, 941, 942, 943, + 944, 945, 946, 947, 948, 949, 950, 951, + 952, 953, 954, 955, 956, 957, 958, 959, + 960, 961, 962, 963, 964, 965, 966, 967, + 968, 969, 970, 971, 972, 973, 974, 975, + 976, 977, 978, 979, 980, 981, 982, 983, + 984, 985, 986, 987, 988, 989, 990, 991, + 992, 993, 994, 995, 996, 997, 998, 999, + 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 1008, 0, 1009, 1010, 1011, 1012, 1013, 1014, + 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, + 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, + 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, + 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, + 0, 0, 525, 1047, 1047, 1047, 1047, 1047, + 1047, 215, 1048, 1049, 1050, 1051, 1052, 1053, + 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, + 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, + 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, + 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, + 1086, 215, 1047, 1087, 0, 0, 77, 77, + 11, 0, 556, 543, 543, 543, 543, 556, + 543, 543, 543, 1088, 556, 543, 543, 543, + 543, 543, 543, 556, 556, 556, 556, 556, + 556, 543, 543, 556, 543, 543, 1088, 1089, + 543, 1090, 1091, 1092, 1093, 1094, 1095, 1096, + 1097, 1098, 1099, 1099, 1100, 1101, 1102, 1103, + 1104, 1105, 1106, 1107, 1105, 543, 556, 1105, + 1098, 0, 0, 0, 0, 0, 0, 0, + 0, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 0, 0, 0, 0, + 1108, 1108, 1108, 1108, 1105, 1105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1109, 1109, 1109, 1109, 1109, 1109, 75, + 75, 1110, 10, 10, 1111, 15, 1112, 77, + 77, 543, 543, 543, 543, 543, 543, 543, + 543, 1113, 1114, 1115, 1112, 1116, 0, 1112, + 1112, 1117, 1117, 1118, 1119, 1120, 1121, 1122, + 1123, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1124, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1125, 1117, 1126, 1127, 1128, 1129, 1113, + 1114, 1115, 1130, 1131, 1132, 1133, 1134, 556, + 543, 543, 543, 543, 543, 556, 543, 543, + 556, 1135, 1135, 1135, 1135, 1135, 1135, 1135, + 1135, 1135, 1135, 10, 1136, 1136, 1112, 1117, + 1117, 1137, 1117, 1117, 1117, 1117, 1138, 1139, + 1140, 1141, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1142, 1143, 1144, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1145, 1146, 1112, 1147, 543, + 543, 543, 543, 543, 543, 543, 1109, 77, + 543, 543, 543, 543, 556, 543, 1124, 1124, + 543, 543, 77, 556, 543, 543, 556, 1117, + 1117, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 1117, 1117, 1117, 1148, 1148, + 1117, 1112, 1112, 1112, 1112, 1112, 1112, 1112, + 1112, 1112, 1112, 1112, 1112, 1112, 1112, 0, + 1149, 1117, 1150, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 543, 556, 543, 543, 556, 543, 543, + 556, 556, 556, 543, 556, 556, 543, 556, + 543, 543, 543, 556, 543, 556, 543, 556, + 543, 556, 543, 543, 0, 0, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1117, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1152, 1152, 1152, 1152, 1152, 1152, 1152, + 1152, 1152, 1152, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 543, 543, 543, 543, + 543, 543, 543, 556, 543, 1153, 1153, 77, + 9, 9, 9, 1153, 0, 0, 556, 1154, + 1154, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 543, + 543, 543, 543, 1153, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 1153, 543, 543, + 543, 1153, 543, 543, 543, 543, 543, 0, + 0, 1105, 1105, 1105, 1105, 1105, 1105, 1105, + 1105, 1105, 1105, 1105, 1105, 1105, 1105, 1105, + 0, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 556, 556, 556, 0, 0, 1105, + 0, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 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, 0, 0, 0, 0, + 0, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 0, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 556, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 1109, 556, 543, 543, 556, + 543, 543, 556, 543, 543, 543, 556, 556, + 556, 1127, 1128, 1129, 543, 543, 543, 556, + 543, 543, 556, 556, 543, 543, 543, 543, + 543, 1151, 1151, 1151, 1155, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 1156, 1157, 341, 341, 341, 341, 341, + 341, 1158, 1159, 341, 1160, 1161, 341, 341, + 341, 341, 341, 1151, 1155, 1162, 341, 1155, + 1155, 1155, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1155, 1155, 1155, 1155, 1163, 1155, + 1155, 341, 543, 556, 543, 543, 1151, 1151, + 1151, 1164, 1165, 1166, 1167, 1168, 1169, 1170, + 1171, 341, 341, 1151, 1151, 1047, 1047, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1047, 525, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1151, 1155, 1155, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 0, + 341, 341, 0, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 0, 0, 0, 341, + 341, 341, 341, 0, 0, 1173, 341, 1174, + 1155, 1155, 1151, 1151, 1151, 1151, 0, 0, + 1175, 1155, 0, 0, 1176, 1177, 1163, 341, + 0, 0, 0, 0, 0, 0, 0, 0, + 1178, 0, 0, 0, 0, 1179, 1180, 0, + 1181, 341, 341, 1151, 1151, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 341, 341, 11, 11, 1182, 1182, 1182, + 1182, 1182, 1182, 841, 11, 341, 1047, 543, + 0, 0, 1151, 1151, 1155, 0, 341, 341, + 341, 341, 341, 341, 0, 0, 0, 0, + 341, 341, 0, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 1183, 0, 341, 1184, + 0, 341, 341, 0, 0, 1173, 0, 1155, + 1155, 1155, 1151, 1151, 0, 0, 0, 0, + 1151, 1151, 0, 0, 1151, 1151, 1163, 0, + 0, 0, 1151, 0, 0, 0, 0, 0, + 0, 0, 1185, 1186, 1187, 341, 0, 1188, + 0, 0, 0, 0, 0, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1151, 1151, 341, 341, 341, 1151, 1047, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1151, 1151, 1155, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 341, 341, 341, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 0, 341, 341, + 341, 341, 341, 0, 0, 1173, 341, 1155, + 1155, 1155, 1151, 1151, 1151, 1151, 1151, 0, + 1151, 1151, 1155, 0, 1155, 1155, 1163, 0, + 0, 341, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 1151, 1151, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1047, 11, 0, 0, 0, 0, 0, + 0, 0, 341, 1151, 1151, 1151, 1151, 1151, + 1151, 0, 1151, 1155, 1155, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 0, + 341, 341, 0, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 0, 341, 341, + 341, 341, 341, 0, 0, 1173, 341, 1189, + 1151, 1155, 1151, 1151, 1151, 1151, 0, 0, + 1190, 1191, 0, 0, 1192, 1193, 1163, 0, + 0, 0, 0, 0, 0, 0, 0, 1194, + 1195, 0, 0, 0, 0, 1196, 1197, 0, + 341, 341, 341, 1151, 1151, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 841, 341, 1182, 1182, 1182, 1182, 1182, + 1182, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1151, 341, 0, 341, 341, + 341, 341, 341, 341, 0, 0, 0, 341, + 341, 341, 0, 1198, 341, 1199, 341, 0, + 0, 0, 341, 341, 0, 341, 0, 341, + 341, 0, 0, 0, 341, 341, 0, 0, + 0, 341, 341, 341, 0, 0, 0, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 0, 0, 0, 0, 1200, + 1155, 1151, 1155, 1155, 0, 0, 0, 1201, + 1202, 1155, 0, 1203, 1204, 1205, 1163, 0, + 0, 341, 0, 0, 0, 0, 0, 0, + 1206, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1182, 1182, 1182, 77, 77, 77, 77, + 77, 77, 11, 77, 0, 0, 0, 0, + 0, 1151, 1155, 1155, 1155, 1151, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 0, 0, 0, 341, 1151, + 1151, 1151, 1155, 1155, 1155, 1155, 0, 1207, + 1151, 1208, 0, 1151, 1151, 1151, 1163, 0, + 0, 0, 0, 0, 0, 0, 1209, 1210, + 0, 341, 341, 341, 0, 0, 0, 0, + 0, 341, 341, 1151, 1151, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 0, 0, 0, 0, 0, 0, 0, + 1047, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 841, 341, 1151, 1155, 1155, 1047, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 341, 341, + 341, 341, 341, 0, 0, 1173, 341, 1155, + 1212, 1213, 1155, 1214, 1155, 1155, 0, 1215, + 1216, 1217, 0, 1218, 1219, 1151, 1163, 0, + 0, 0, 0, 0, 0, 0, 1220, 1221, + 0, 0, 0, 0, 0, 0, 0, 341, + 0, 341, 341, 1151, 1151, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 0, 341, 341, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1151, 1151, 1155, 1155, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1163, 1163, 341, 1222, + 1155, 1155, 1151, 1151, 1151, 1151, 0, 1223, + 1224, 1155, 0, 1225, 1226, 1227, 1163, 1228, + 841, 0, 0, 0, 0, 341, 341, 341, + 1229, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 341, 341, 341, 1151, 1151, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 841, 341, 341, 341, 341, 341, + 341, 0, 0, 1155, 1155, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 0, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 0, 0, 1230, 0, 0, 0, 0, + 1231, 1155, 1155, 1151, 1151, 1151, 0, 1151, + 0, 1155, 1232, 1233, 1155, 1234, 1235, 1236, + 1237, 0, 0, 0, 0, 0, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 0, 0, 1155, 1155, 1047, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1151, 341, 1238, 1151, 1151, 1151, + 1151, 1239, 1239, 1163, 0, 0, 0, 0, + 11, 341, 341, 341, 341, 341, 341, 525, + 1151, 1240, 1240, 1240, 1240, 1151, 1151, 1151, + 1047, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1047, 1047, 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, 341, 341, 0, 341, 0, 341, + 341, 341, 341, 341, 0, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 341, 0, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1151, 341, 1241, 1151, 1151, 1151, + 1151, 1242, 1242, 1163, 1151, 1151, 341, 0, + 0, 341, 341, 341, 341, 341, 0, 525, + 0, 1243, 1243, 1243, 1243, 1151, 1151, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 1244, 1245, 341, + 341, 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, 341, 841, 841, 841, 1047, 1047, 1047, + 1047, 1047, 1047, 1047, 1047, 1246, 1047, 1047, + 1047, 1047, 1047, 1047, 841, 1047, 841, 841, + 841, 556, 556, 841, 841, 841, 841, 841, + 841, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 841, 556, 841, + 556, 841, 1247, 12, 13, 12, 13, 1155, + 1155, 341, 341, 341, 1248, 341, 341, 341, + 341, 0, 341, 341, 341, 341, 1249, 341, + 341, 341, 341, 1250, 341, 341, 341, 341, + 1251, 341, 341, 341, 341, 1252, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1253, 341, 341, 341, 0, 0, + 0, 0, 1254, 1255, 1256, 1257, 1258, 1259, + 1260, 1261, 1262, 1255, 1255, 1255, 1255, 1151, + 1155, 1255, 1263, 543, 543, 1163, 1047, 543, + 543, 341, 341, 341, 341, 341, 1151, 1151, + 1151, 1151, 1151, 1151, 1264, 1151, 1151, 1151, + 1151, 0, 1151, 1151, 1151, 1151, 1265, 1151, + 1151, 1151, 1151, 1266, 1151, 1151, 1151, 1151, + 1267, 1151, 1151, 1151, 1151, 1268, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1269, 1151, 1151, 1151, 0, 841, + 841, 841, 841, 841, 841, 841, 841, 556, + 841, 841, 841, 841, 841, 841, 0, 841, + 841, 1047, 1047, 1047, 1047, 1047, 841, 841, + 841, 841, 1047, 1047, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 1270, 1271, + 341, 341, 341, 341, 1272, 1272, 1151, 1273, + 1151, 1151, 1155, 1151, 1151, 1151, 1151, 1151, + 1173, 1272, 1163, 1163, 1155, 1155, 1151, 1151, + 341, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1047, 1047, 1047, 1047, 1047, + 1047, 341, 341, 341, 341, 341, 341, 1155, + 1155, 1151, 1151, 341, 341, 341, 341, 1151, + 1151, 1151, 341, 1272, 1272, 1272, 341, 341, + 1272, 1272, 1272, 1272, 1272, 1272, 1272, 341, + 341, 341, 1151, 1151, 1151, 1151, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 1151, 1272, 1155, 1151, 1151, + 1272, 1272, 1272, 1272, 1272, 1272, 556, 341, + 1272, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1272, 1272, 1272, 1151, 841, + 841, 1274, 1275, 1276, 1277, 1278, 1279, 1280, + 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, + 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, + 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, + 1305, 1306, 1307, 1308, 1309, 1310, 1311, 0, + 1312, 0, 0, 0, 0, 0, 1313, 0, + 0, 1314, 1315, 1316, 1317, 1318, 1319, 1320, + 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, + 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, + 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, + 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, + 1353, 1354, 1355, 1356, 1047, 1357, 1358, 1359, + 1360, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1362, 1363, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 0, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 0, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 0, 0, 543, 543, + 543, 1047, 1047, 1047, 1047, 1047, 1047, 1047, + 1047, 1047, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 0, 0, 0, 0, 0, + 0, 1366, 1367, 1368, 1369, 1370, 1371, 1372, + 1373, 1374, 1375, 1376, 1377, 1378, 1379, 1380, + 1381, 1382, 1383, 1384, 1385, 1386, 1387, 1388, + 1389, 1390, 1391, 1392, 1393, 1394, 1395, 1396, + 1397, 1398, 1399, 1400, 1401, 1402, 1403, 1404, + 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, + 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, + 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, + 1429, 1430, 1431, 1432, 1433, 1434, 1435, 1436, + 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, + 1445, 1446, 1447, 1448, 1449, 1450, 1451, 0, + 0, 1452, 1453, 1454, 1455, 1456, 1457, 0, + 0, 1087, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 841, 1047, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 8, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 12, 13, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1047, 1047, 1047, 1458, + 1458, 1458, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 341, + 341, 341, 341, 1151, 1151, 1163, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 1151, 1151, 1163, 1047, 1047, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 1151, 1151, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 341, + 341, 341, 0, 1151, 1151, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 577, 577, 1155, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1151, + 1155, 1155, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1163, 1151, 1047, 1047, 1047, + 525, 1047, 1047, 1047, 11, 341, 543, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 0, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 1087, + 9, 9, 9, 9, 577, 577, 577, 1459, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 525, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 1151, 1151, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1089, 341, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 1151, 1151, 1151, 1155, 1155, 1155, 1155, + 1151, 1151, 1155, 1155, 1155, 0, 0, 0, + 0, 1155, 1155, 1151, 1155, 1155, 1155, 1155, + 1155, 1155, 1088, 543, 556, 0, 0, 0, + 0, 77, 0, 0, 0, 9, 9, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1182, 0, 0, 0, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 543, 556, 1155, 1155, 1151, 0, 0, 1047, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 1155, 1151, + 1155, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 0, 1163, 1272, 1151, 1272, 1272, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1155, 1155, + 1155, 1155, 1155, 1155, 1151, 1151, 543, 543, + 543, 543, 543, 543, 543, 543, 0, 0, + 556, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 1047, 1047, 1047, 1047, 1047, 1047, 1047, + 525, 1047, 1047, 1047, 1047, 1047, 1047, 0, + 0, 543, 543, 543, 543, 543, 556, 556, + 556, 556, 556, 556, 543, 543, 556, 842, + 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, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1151, 1151, 1151, 1151, 1155, 1460, 1461, + 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, + 341, 341, 1470, 1471, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 1173, 1472, 1151, + 1151, 1151, 1151, 1473, 1474, 1475, 1476, 1477, + 1478, 1479, 1480, 1481, 1482, 1483, 341, 341, + 341, 341, 341, 341, 341, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1047, 1047, 1047, 1047, 1047, + 1047, 1047, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 543, 556, 543, 543, + 543, 543, 543, 543, 543, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 0, 0, + 0, 1151, 1151, 1155, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1155, 1151, 1151, 1151, 1151, 1155, + 1155, 1151, 1151, 1483, 1163, 1151, 1151, 341, + 341, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 1173, + 1155, 1151, 1151, 1155, 1155, 1155, 1151, 1155, + 1151, 1151, 1151, 1483, 1483, 0, 0, 0, + 0, 0, 0, 0, 0, 1047, 1047, 1047, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1155, 1155, 1151, + 1173, 0, 0, 0, 1047, 1047, 1047, 1047, + 1047, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 341, 341, + 341, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 525, 525, 525, 525, 525, 525, 1047, + 1047, 1484, 1485, 1486, 1487, 1488, 1488, 1489, + 1490, 1491, 0, 0, 0, 0, 0, 0, + 0, 1492, 1493, 1494, 1495, 1496, 1497, 1498, + 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, + 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, + 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, + 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, + 1531, 1532, 1533, 1534, 0, 0, 1535, 1536, + 1537, 1047, 1047, 1047, 1047, 1047, 1047, 1047, + 1047, 0, 0, 0, 0, 0, 0, 0, + 0, 543, 543, 543, 1047, 569, 556, 556, + 556, 556, 556, 543, 543, 556, 556, 556, + 556, 543, 1155, 569, 569, 569, 569, 569, + 569, 569, 341, 341, 341, 341, 556, 341, + 341, 341, 341, 341, 341, 543, 341, 341, + 1155, 543, 543, 341, 0, 0, 0, 0, + 0, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 1538, 1539, 1540, + 525, 1541, 1542, 1543, 1544, 1545, 1546, 1547, + 1548, 1549, 1550, 1551, 525, 1552, 1553, 1554, + 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, + 1563, 1564, 1565, 1566, 1567, 1568, 1569, 525, + 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, + 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, + 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, + 1594, 1595, 1596, 1597, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 1598, 1599, 215, 215, 215, 1600, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 1601, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 1602, 1603, 1604, 1605, + 1568, 1606, 1607, 1608, 1609, 1610, 1611, 1612, + 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, + 1621, 1622, 1623, 1624, 1625, 1626, 1627, 1628, + 1629, 1630, 1631, 1632, 1633, 1634, 1635, 1636, + 1637, 543, 543, 556, 543, 543, 543, 543, + 543, 543, 543, 556, 543, 543, 579, 1638, + 556, 558, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 555, + 1089, 1089, 556, 0, 543, 578, 556, 543, + 556, 1639, 1640, 1641, 1642, 1643, 1644, 1645, + 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, + 1654, 1655, 1656, 1657, 1658, 1659, 1660, 1661, + 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, + 1670, 1671, 1672, 1673, 1674, 1675, 1676, 1677, + 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, + 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, + 1694, 1695, 1696, 1697, 1698, 1699, 1700, 1701, + 1702, 1703, 1704, 1705, 1706, 1707, 1708, 1709, + 1710, 1711, 1712, 1713, 1714, 1715, 1716, 1717, + 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, + 1726, 1727, 1728, 1729, 1730, 1731, 1732, 1733, + 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, + 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749, + 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, + 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, + 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, + 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, + 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, + 1790, 1791, 1792, 1793, 1794, 215, 215, 1795, + 215, 1796, 1797, 1798, 1799, 1800, 1801, 1802, + 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, + 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, + 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, + 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1834, + 1835, 1836, 1837, 1838, 1839, 1840, 1841, 1842, + 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850, + 1851, 1852, 1853, 1854, 1855, 1856, 1857, 1858, + 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, + 1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, + 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, + 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, + 1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, + 1899, 1900, 1901, 1902, 1903, 1904, 1905, 1906, + 1907, 1908, 1909, 1910, 1911, 1912, 1913, 0, + 0, 1914, 1915, 1916, 1917, 1918, 1919, 0, + 0, 1920, 1921, 1922, 1923, 1924, 1925, 1926, + 1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934, + 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, + 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, + 1951, 1952, 1953, 1954, 1955, 1956, 1957, 0, + 0, 1958, 1959, 1960, 1961, 1962, 1963, 0, + 0, 1964, 1965, 1966, 1967, 1968, 1969, 1970, + 1971, 0, 1972, 0, 1973, 0, 1974, 0, + 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, + 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, + 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 0, + 0, 2006, 2007, 2008, 2009, 2010, 2011, 2012, + 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, + 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, + 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, + 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, + 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, + 2053, 2054, 2055, 2056, 2057, 2058, 0, 2059, + 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, + 2068, 2069, 2070, 2071, 2072, 2073, 0, 2074, + 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, + 2083, 2084, 2085, 2086, 2087, 0, 0, 2088, + 2089, 2090, 2091, 2092, 2093, 0, 2094, 2095, + 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, + 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, + 2112, 0, 0, 2113, 2114, 2115, 0, 2116, + 2117, 2118, 2119, 2120, 2121, 2122, 2123, 2124, + 0, 2125, 2126, 2127, 2127, 2127, 2127, 2127, + 2128, 2127, 2127, 2127, 1459, 2129, 2130, 2131, + 2132, 1087, 2133, 1087, 1087, 1087, 1087, 9, + 2134, 2135, 2136, 2137, 2135, 2135, 2136, 2137, + 2135, 9, 9, 9, 9, 2138, 2139, 2140, + 9, 2141, 2142, 2143, 2144, 2145, 2146, 2147, + 76, 10, 10, 10, 2148, 2149, 9, 2150, + 2151, 9, 81, 93, 9, 2152, 9, 2153, + 48, 48, 9, 9, 9, 2154, 12, 13, + 2155, 2156, 2157, 9, 9, 9, 9, 9, + 9, 9, 9, 75, 9, 48, 9, 9, + 2158, 9, 9, 9, 9, 9, 9, 9, + 2127, 1459, 1459, 1459, 1459, 1459, 0, 2159, + 2160, 2161, 2162, 1459, 1459, 1459, 1459, 1459, + 1459, 2163, 2164, 0, 0, 2165, 2166, 2167, + 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, + 2176, 2177, 2178, 2179, 2180, 2181, 2182, 2183, + 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, + 0, 2192, 2193, 2194, 2195, 2196, 2197, 2198, + 2199, 2200, 2201, 2202, 2203, 2204, 0, 0, + 0, 11, 11, 11, 11, 11, 11, 11, + 11, 2205, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, + 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 543, 543, 569, 569, 543, 543, 543, + 543, 569, 569, 569, 543, 543, 842, 842, + 842, 842, 543, 842, 842, 842, 569, 569, + 543, 556, 543, 569, 569, 556, 556, 556, + 556, 543, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2206, 2207, 2208, 2209, 77, 2210, 2211, + 2212, 77, 2213, 2214, 2215, 2215, 2215, 2216, + 2217, 2218, 2218, 2219, 2220, 77, 2221, 2222, + 77, 75, 2223, 2224, 2225, 2225, 2225, 77, + 77, 2226, 2227, 2228, 77, 2229, 77, 2230, + 77, 2229, 77, 2231, 2232, 2233, 2208, 84, + 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, + 2242, 2243, 2244, 77, 2245, 2246, 2247, 2248, + 2249, 2250, 75, 75, 75, 75, 2251, 2252, + 2234, 2253, 2254, 77, 75, 77, 77, 2255, + 841, 2256, 2257, 2258, 2259, 2260, 2261, 2262, + 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, + 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, + 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, + 2287, 2288, 2289, 2290, 2291, 2292, 2293, 2294, + 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, + 2303, 1458, 1458, 1458, 2304, 2305, 1458, 1458, + 1458, 1458, 2306, 77, 77, 0, 0, 0, + 0, 2307, 75, 2308, 75, 2309, 79, 79, + 79, 79, 79, 2310, 2311, 77, 77, 77, + 77, 75, 77, 77, 75, 77, 77, 75, + 77, 77, 79, 79, 77, 77, 77, 2312, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 2313, 2314, + 2315, 2316, 77, 2317, 77, 2318, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 2319, 2319, 2320, 2321, 75, 75, + 75, 2322, 2323, 2319, 2324, 2325, 2319, 75, + 75, 75, 2319, 14, 85, 75, 2319, 2319, + 75, 75, 75, 2319, 2319, 2319, 2319, 75, + 2319, 2319, 2319, 2319, 2326, 2327, 2328, 2329, + 75, 75, 75, 75, 2319, 2330, 2331, 2319, + 2332, 2333, 2319, 2319, 2319, 75, 75, 75, + 75, 75, 2319, 75, 2319, 2334, 2319, 2319, + 2319, 2319, 2335, 2319, 2336, 2337, 2338, 2319, + 2339, 2340, 2341, 2319, 2319, 2319, 2342, 75, + 75, 75, 75, 2319, 2319, 2319, 2319, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 2319, 2343, 2344, 2345, 75, 2346, 2347, 2319, + 2319, 2319, 2319, 2319, 2319, 75, 2348, 2349, + 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, + 2358, 2359, 2360, 2361, 2362, 2363, 2364, 2319, + 2319, 2365, 2366, 2367, 2368, 2369, 2370, 2371, + 2372, 2373, 2374, 2319, 2319, 2319, 75, 75, + 2319, 2319, 2375, 2376, 75, 75, 75, 75, + 75, 2319, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 2377, 2319, 75, 75, 2319, + 2319, 2378, 2379, 2319, 2380, 2381, 2382, 2383, + 2384, 2319, 2319, 2385, 2386, 2387, 2388, 2319, + 2319, 2319, 75, 75, 75, 75, 75, 2319, + 2319, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 2319, 2319, 2319, 2319, 2319, 75, + 75, 2319, 2319, 75, 75, 75, 75, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2389, 2390, 2391, 2392, 2319, 2319, 2319, + 2319, 2319, 2319, 2393, 2394, 2395, 2396, 75, + 75, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 77, 77, 77, 77, 77, 77, 77, + 77, 12, 13, 12, 13, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 2397, 2397, 77, 77, 77, + 77, 2319, 2319, 77, 77, 77, 77, 77, + 77, 79, 2398, 2399, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 77, 75, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 79, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 841, 77, + 77, 77, 77, 77, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 79, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 75, 75, 75, + 75, 75, 75, 77, 77, 77, 77, 77, + 77, 77, 2397, 2397, 2397, 2397, 79, 79, + 79, 2397, 79, 79, 2397, 77, 77, 77, + 77, 79, 79, 79, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 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, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2400, 2401, 2402, 2403, 2404, 2405, 2406, + 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, + 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, + 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, + 2431, 2432, 2433, 2434, 2435, 2436, 2437, 2438, + 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446, + 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, + 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, + 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, + 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, + 2479, 2480, 2481, 2482, 2483, 2484, 2485, 2486, + 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, + 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, + 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, + 2511, 2512, 2513, 2514, 2515, 2516, 2517, 2518, + 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, + 2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, + 2535, 2536, 2537, 2538, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 79, 79, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 79, + 75, 77, 77, 77, 77, 77, 77, 77, + 77, 79, 75, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 75, 75, 75, 2539, 2539, 2540, 2540, + 75, 79, 79, 79, 79, 79, 79, 77, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 77, 2397, 2397, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 2539, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 2397, 79, 79, 79, 79, 79, 79, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 79, 79, 79, 2397, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 2397, 79, 79, 79, 79, 79, + 79, 79, 79, 2397, 2397, 2541, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 2397, 2397, + 79, 79, 79, 79, 79, 2397, 2397, 79, + 79, 79, 79, 79, 79, 79, 79, 2397, + 79, 79, 79, 79, 79, 2397, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 2397, 79, 79, 79, 79, + 79, 79, 79, 2397, 2397, 79, 2397, 79, + 79, 79, 79, 2397, 79, 79, 2397, 79, + 79, 79, 79, 79, 79, 79, 2397, 77, + 77, 79, 79, 2397, 2397, 79, 79, 79, + 79, 79, 79, 79, 77, 79, 77, 79, + 77, 77, 77, 77, 77, 77, 79, 77, + 77, 77, 79, 77, 77, 77, 77, 77, + 77, 2397, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 79, 79, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 79, 77, 77, + 79, 77, 77, 77, 77, 2397, 77, 2397, + 77, 77, 77, 77, 2397, 2397, 2397, 77, + 2397, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 79, 79, 79, 79, + 79, 12, 13, 12, 13, 12, 13, 12, + 13, 12, 13, 12, 13, 12, 13, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 77, 2397, 2397, + 2397, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 79, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 2397, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 2397, 2319, 75, 75, 2319, 2319, 12, 13, + 75, 2319, 2319, 75, 2319, 2319, 2319, 75, + 75, 75, 75, 75, 2319, 2319, 2319, 2319, + 75, 75, 75, 75, 75, 2319, 2319, 2319, + 75, 75, 75, 2319, 2319, 2319, 2319, 12, + 13, 12, 13, 12, 13, 12, 13, 12, + 13, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 2539, 2539, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 12, 13, 12, 13, 12, 13, + 12, 13, 75, 75, 2319, 2319, 2319, 2319, + 2319, 2319, 75, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 75, 75, 75, 75, 75, 75, 75, + 75, 2319, 75, 75, 75, 75, 75, 75, + 75, 2319, 2319, 2319, 2319, 2319, 2319, 75, + 75, 75, 2319, 75, 75, 75, 75, 2319, + 2319, 2319, 2319, 2319, 75, 2319, 2319, 75, + 75, 12, 13, 12, 13, 2319, 75, 75, + 75, 75, 2319, 75, 2319, 2319, 2319, 75, + 75, 2319, 2319, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 2319, 2319, 2319, + 2319, 2319, 2319, 75, 75, 12, 13, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 2319, 2319, 2542, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 75, 2319, + 2319, 2319, 2319, 75, 75, 2319, 75, 2319, + 75, 75, 2319, 75, 2319, 2319, 2319, 2319, + 75, 75, 75, 75, 75, 2319, 2319, 75, + 75, 75, 75, 75, 75, 2319, 2319, 2319, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 2319, 2319, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 2319, 2319, 75, + 75, 75, 75, 2319, 2319, 2319, 2319, 75, + 2319, 2319, 75, 75, 2319, 2543, 2544, 2545, + 75, 75, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 75, 75, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 75, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 2319, 2319, 2319, 2319, 2319, 2319, 2319, 2319, + 75, 75, 75, 75, 75, 2546, 2547, 2319, + 75, 75, 75, 2319, 2319, 2319, 2319, 2319, + 75, 75, 75, 75, 75, 2319, 2319, 2319, + 75, 75, 75, 75, 2319, 75, 75, 75, + 2319, 2319, 2319, 2319, 2319, 75, 2319, 75, + 75, 77, 77, 77, 77, 77, 79, 79, + 79, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 2397, 2397, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 77, 77, + 75, 75, 75, 75, 75, 75, 77, 77, + 77, 2397, 77, 77, 77, 77, 2397, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 0, 0, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 2548, + 77, 2549, 2550, 2551, 2552, 2553, 2554, 2555, + 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, + 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, + 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, + 2580, 2581, 2582, 2583, 2584, 2585, 2586, 2587, + 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, + 0, 2596, 2597, 2598, 2599, 2600, 2601, 2602, + 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, + 2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618, + 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626, + 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, + 2635, 2636, 2637, 2638, 2639, 2640, 2641, 2642, + 0, 2643, 2644, 2645, 2646, 2647, 2648, 2649, + 2650, 2651, 2652, 2653, 2654, 2655, 2656, 2657, + 2658, 2659, 215, 2660, 2661, 215, 2662, 2663, + 215, 215, 215, 215, 215, 2664, 2665, 2666, + 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, + 2675, 2676, 2677, 2678, 2679, 2680, 2681, 2682, + 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, + 2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, + 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, + 2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, + 2715, 2716, 2717, 2718, 2719, 2720, 2721, 2722, + 2723, 2724, 2725, 2726, 2727, 2728, 2729, 2730, + 2731, 2732, 2733, 2734, 2735, 2736, 2737, 2738, + 2739, 2740, 2741, 2742, 2743, 2744, 2745, 2746, + 2747, 2748, 2749, 2750, 2751, 2752, 2753, 2754, + 2755, 2756, 2757, 2758, 2759, 2760, 2761, 2762, + 2763, 2764, 2765, 2766, 2767, 215, 77, 77, + 77, 77, 77, 77, 2768, 2769, 2770, 2771, + 543, 543, 543, 2772, 2773, 0, 0, 0, + 0, 0, 9, 9, 9, 9, 1211, 9, + 9, 2774, 2775, 2776, 2777, 2778, 2779, 2780, + 2781, 2782, 2783, 2784, 2785, 2786, 2787, 2788, + 2789, 2790, 2791, 2792, 2793, 2794, 2795, 2796, + 2797, 2798, 2799, 2800, 2801, 2802, 2803, 2804, + 2805, 2806, 2807, 2808, 2809, 2810, 2811, 0, + 2812, 0, 0, 0, 0, 0, 2813, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 0, 0, 0, 0, 0, 0, 0, + 2814, 1047, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1163, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 9, 9, 81, 93, 81, 93, 9, + 9, 9, 81, 93, 9, 81, 93, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 1087, 9, 9, 1087, 9, 81, 93, 9, + 9, 81, 93, 12, 13, 12, 13, 12, + 13, 12, 13, 9, 9, 9, 9, 9, + 524, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 1087, 1087, 9, 9, 9, + 9, 1087, 9, 2137, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 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, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 0, 2815, 2815, 2815, 2815, + 2816, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2817, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2818, 2819, 2820, 2821, 2822, 2823, 2824, + 2825, 2826, 2827, 2828, 2829, 2830, 2831, 2832, + 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, + 2841, 2842, 2843, 2844, 2845, 2846, 2847, 2848, + 2849, 2850, 2851, 2852, 2853, 2854, 2855, 2856, + 2857, 2858, 2859, 2860, 2861, 2862, 2863, 2864, + 2865, 2866, 2867, 2868, 2869, 2870, 2871, 2872, + 2873, 2874, 2875, 2876, 2877, 2878, 2879, 2880, + 2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, + 2889, 2890, 2891, 2892, 2893, 2894, 2895, 2896, + 2897, 2898, 2899, 2900, 2901, 2902, 2903, 2904, + 2905, 2906, 2907, 2908, 2909, 2910, 2911, 2912, + 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, + 2921, 2922, 2923, 2924, 2925, 2926, 2927, 2928, + 2929, 2930, 2931, 2932, 2933, 2934, 2935, 2936, + 2937, 2938, 2939, 2940, 2941, 2942, 2943, 2944, + 2945, 2946, 2947, 2948, 2949, 2950, 2951, 2952, + 2953, 2954, 2955, 2956, 2957, 2958, 2959, 2960, + 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, + 2969, 2970, 2971, 2972, 2973, 2974, 2975, 2976, + 2977, 2978, 2979, 2980, 2981, 2982, 2983, 2984, + 2985, 2986, 2987, 2988, 2989, 2990, 2991, 2992, + 2993, 2994, 2995, 2996, 2997, 2998, 2999, 3000, + 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, + 3009, 3010, 3011, 3012, 3013, 3014, 3015, 3016, + 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, + 3025, 3026, 3027, 3028, 3029, 3030, 3031, 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, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 0, 0, 0, + 0, 3032, 3033, 3033, 3033, 2815, 3034, 3035, + 3036, 3037, 3038, 3037, 3038, 3037, 3038, 3037, + 3038, 3037, 3038, 2815, 2815, 3037, 3038, 3037, + 3038, 3037, 3038, 3037, 3038, 3039, 3040, 3041, + 3041, 2815, 3036, 3036, 3036, 3036, 3036, 3036, + 3036, 3036, 3036, 3042, 1089, 555, 1088, 3043, + 3043, 3044, 3034, 3034, 3034, 3034, 3034, 3045, + 2815, 3046, 3047, 3048, 3034, 3035, 3049, 2815, + 77, 0, 3035, 3035, 3035, 3035, 3035, 3050, + 3035, 3035, 3035, 3035, 3051, 3052, 3053, 3054, + 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062, + 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, + 3071, 3072, 3073, 3074, 3035, 3075, 3076, 3077, + 3078, 3079, 3080, 3035, 3035, 3035, 3035, 3035, + 3081, 3082, 3083, 3084, 3085, 3086, 3087, 3088, + 3089, 3090, 3091, 3092, 3093, 3094, 3095, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3096, 3035, 3035, + 0, 0, 3097, 3098, 3099, 3100, 3101, 3102, + 3103, 3039, 3035, 3035, 3035, 3035, 3035, 3104, + 3035, 3035, 3035, 3035, 3105, 3106, 3107, 3108, + 3109, 3110, 3111, 3112, 3113, 3114, 3115, 3116, + 3117, 3118, 3119, 3120, 3121, 3122, 3123, 3124, + 3125, 3126, 3127, 3128, 3035, 3129, 3130, 3131, + 3132, 3133, 3134, 3035, 3035, 3035, 3035, 3035, + 3135, 3136, 3137, 3138, 3139, 3140, 3141, 3142, + 3143, 3144, 3145, 3146, 3147, 3148, 3149, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3150, 3151, 3152, 3153, 3035, 3154, 3035, 3035, + 3155, 3156, 3157, 3158, 3033, 3034, 3159, 3160, + 3161, 0, 0, 0, 0, 0, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 0, 3162, 3163, 3164, 3165, 3166, 3167, + 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3175, + 3176, 3177, 3178, 3179, 3180, 3181, 3182, 3183, + 3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, + 3192, 3193, 3194, 3195, 3196, 3197, 3198, 3199, + 3200, 3201, 3202, 3203, 3204, 3205, 3206, 3207, + 3208, 3209, 3210, 3211, 3212, 3213, 3214, 3215, + 3216, 3217, 3218, 3219, 3220, 3221, 3222, 3223, + 3224, 3225, 3226, 3227, 3228, 3229, 3230, 3231, + 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3239, + 3240, 3241, 3242, 3243, 3244, 3245, 3246, 3247, + 3248, 3249, 3250, 3251, 3252, 3253, 3254, 3255, + 0, 3256, 3256, 3257, 3258, 3259, 3260, 3261, + 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269, + 3270, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 0, 0, 0, 0, + 0, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3271, 3272, 3273, 3274, 3275, 3276, 3277, + 3278, 3279, 3280, 3281, 3282, 3283, 3284, 3285, + 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, + 3294, 3295, 3296, 3297, 3298, 3299, 3300, 3301, + 0, 3302, 3303, 3304, 3305, 3306, 3307, 3308, + 3309, 3310, 3311, 3312, 3313, 3314, 3315, 3316, + 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, + 3325, 3326, 3327, 3328, 3329, 3330, 3331, 3332, + 3333, 3334, 3335, 3336, 3337, 3338, 3339, 3340, + 3341, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 3342, 3343, 3344, 3345, 3346, 3347, 3348, + 3349, 3350, 3351, 3352, 3353, 3354, 3355, 3356, + 3357, 3358, 3359, 3360, 3361, 3362, 3363, 3364, + 3365, 3366, 3367, 3368, 3369, 3370, 3371, 3372, + 3373, 3374, 3375, 3376, 3377, 3378, 3379, 3380, + 3381, 3382, 3383, 3384, 3385, 3386, 3387, 3388, + 3256, 3389, 3390, 3391, 3392, 3393, 3394, 3395, + 3396, 3397, 3398, 3399, 3400, 3401, 3402, 3403, + 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, + 3412, 3413, 3414, 3415, 3416, 3417, 3418, 3419, + 3420, 3421, 3422, 3423, 3424, 3425, 3426, 3427, + 3428, 3429, 3430, 3431, 3432, 3433, 3434, 3435, + 3436, 3437, 3438, 3439, 3440, 3441, 3442, 3443, + 3444, 3445, 3446, 3447, 3448, 3449, 3450, 3451, + 3452, 3453, 3454, 3455, 3456, 3457, 3458, 3459, + 3460, 3461, 3462, 3463, 3464, 3465, 3466, 3467, + 3468, 3469, 3470, 3471, 3472, 3473, 3474, 3475, + 3476, 3477, 3478, 3479, 3480, 3481, 3482, 3483, + 3484, 3485, 3486, 3487, 3488, 3489, 3490, 3491, + 3492, 3493, 3494, 3495, 3496, 3497, 3498, 3499, + 3500, 3501, 3502, 3503, 3504, 3505, 3506, 3507, + 3508, 3509, 3510, 3511, 3512, 3513, 3514, 3515, + 3516, 3517, 3518, 3519, 3520, 3521, 3522, 3523, + 3524, 3525, 3526, 3527, 3528, 3529, 3530, 3531, + 3532, 3533, 3534, 3535, 3536, 3537, 3538, 3539, + 3540, 3541, 3542, 3543, 3544, 3545, 3546, 3547, + 3548, 3549, 3550, 3551, 3552, 3553, 3554, 3555, + 3556, 3557, 3558, 3559, 3560, 3561, 3562, 3563, + 3564, 3565, 3566, 3567, 3568, 3569, 3570, 3571, + 3572, 3573, 3574, 3575, 3576, 3577, 3578, 3579, + 3580, 3581, 3582, 3583, 3584, 3585, 3586, 3587, + 3588, 3589, 3590, 3591, 3592, 3593, 3594, 3595, + 3596, 3597, 3598, 3599, 3600, 3601, 3602, 3603, + 3604, 3605, 3606, 3607, 3608, 3609, 3610, 3611, + 3612, 3613, 3614, 3615, 3616, 3617, 3618, 3619, + 3620, 3621, 3622, 3623, 3624, 3625, 3626, 3627, + 3628, 3629, 3630, 3631, 3632, 3633, 3634, 3635, + 3636, 3637, 3638, 3639, 3640, 3641, 3642, 3643, + 3644, 3645, 3646, 3647, 3648, 3649, 3650, 3651, + 3652, 3653, 3654, 3655, 3656, 3657, 3658, 3659, + 3660, 3661, 3662, 3663, 3664, 3665, 3666, 3667, + 3668, 3669, 3670, 3671, 3672, 3673, 3674, 3675, + 3676, 3677, 3678, 3679, 3680, 3681, 3682, 3683, + 3684, 3685, 3686, 3687, 3688, 3689, 3690, 3691, + 3692, 3693, 3694, 3695, 3696, 3697, 3698, 3699, + 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, + 3708, 3709, 3710, 3711, 3712, 3713, 3714, 3715, + 3716, 3717, 3718, 3719, 3720, 3721, 3722, 3723, + 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, + 3732, 3733, 3734, 3735, 3736, 3737, 3738, 3739, + 3740, 3741, 3742, 3743, 3744, 3745, 3746, 3747, + 3748, 3749, 3750, 3751, 3752, 3753, 3754, 3755, + 3756, 3757, 3758, 3759, 3760, 3761, 3762, 3763, + 3764, 3765, 3766, 3767, 3768, 3769, 3770, 3771, + 3772, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3034, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 0, 0, + 0, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 2815, 2815, 2815, 2815, 2815, 2815, 2815, 2815, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 525, 525, 525, 525, 525, 525, 1047, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 525, 9, 9, + 9, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 341, 341, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3773, 3774, 3775, 3776, 3777, 3778, 3779, + 3780, 3781, 3782, 3783, 3784, 3785, 3786, 3787, + 3788, 3789, 3790, 3791, 3792, 3793, 3794, 3795, + 3796, 3797, 3798, 3799, 3800, 3801, 3802, 3803, + 3804, 3805, 3806, 3807, 3808, 3809, 3810, 3811, + 3812, 3813, 3814, 3815, 3816, 3817, 3818, 341, + 543, 842, 842, 842, 9, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 9, + 524, 3819, 3820, 3821, 3822, 3823, 3824, 3825, + 3826, 3827, 3828, 3829, 3830, 3831, 3832, 3833, + 3834, 3835, 3836, 3837, 3838, 3839, 3840, 3841, + 3842, 3843, 3844, 3845, 3846, 3847, 3848, 543, + 543, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 543, 543, 1047, 1047, 1047, 1047, 1047, + 1047, 0, 0, 0, 0, 0, 0, 0, + 0, 526, 526, 526, 526, 526, 526, 526, + 526, 526, 526, 526, 526, 526, 526, 526, + 526, 526, 526, 526, 526, 526, 526, 526, + 524, 524, 524, 524, 524, 524, 524, 524, + 524, 526, 526, 3849, 3850, 3851, 3852, 3853, + 3854, 3855, 3856, 3857, 3858, 3859, 3860, 3861, + 3862, 215, 215, 3863, 3864, 3865, 3866, 3867, + 3868, 3869, 3870, 3871, 3872, 3873, 3874, 3875, + 3876, 3877, 3878, 3879, 3880, 3881, 3882, 3883, + 3884, 3885, 3886, 3887, 3888, 3889, 3890, 3891, + 3892, 3893, 3894, 3895, 3896, 3897, 3898, 3899, + 3900, 3901, 3902, 3903, 3904, 3905, 3906, 3907, + 3908, 3909, 3910, 3911, 3912, 3913, 3914, 3915, + 3916, 3917, 3918, 3919, 3920, 3921, 3922, 3923, + 3924, 3925, 215, 215, 215, 215, 215, 215, + 215, 215, 3926, 3927, 3928, 3929, 3930, 3931, + 3932, 3933, 3934, 3935, 3936, 3937, 3938, 3939, + 3940, 524, 3941, 3941, 3942, 3943, 3944, 215, + 341, 3945, 3946, 3947, 3948, 3949, 215, 3950, + 3951, 3952, 3953, 3954, 3955, 3956, 3957, 3958, + 3959, 3960, 3961, 3962, 3963, 3964, 3965, 3966, + 3967, 3968, 3969, 3970, 3971, 3972, 3973, 3974, + 215, 3975, 3976, 3977, 3978, 3979, 3980, 3981, + 3982, 3983, 3984, 3985, 3986, 3987, 3988, 3989, + 3990, 0, 0, 3991, 3992, 3993, 3994, 3995, + 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, + 341, 3996, 3997, 215, 341, 341, 341, 341, + 341, 341, 341, 1151, 341, 341, 341, 1163, + 341, 341, 341, 341, 1151, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1155, 1155, 1151, 1151, + 1155, 77, 77, 77, 77, 0, 0, 0, + 0, 1182, 1182, 1182, 1182, 1182, 1182, 841, + 841, 11, 84, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 9, 9, 9, + 9, 0, 0, 0, 0, 0, 0, 0, + 0, 1155, 1155, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1163, 1151, 0, + 0, 0, 0, 0, 0, 0, 0, 1047, + 1047, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 341, 341, 341, 341, 341, + 341, 1047, 1047, 1047, 341, 1047, 341, 341, + 1151, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 1151, + 1151, 1151, 1151, 1151, 556, 556, 556, 1047, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1155, 1483, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1047, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, + 1361, 1361, 1361, 1361, 1361, 1361, 0, 0, + 0, 1151, 1151, 1151, 1155, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1173, 1155, 1155, 1151, + 1151, 1151, 1151, 1155, 1155, 1151, 1151, 1155, + 1155, 1483, 1047, 1047, 1047, 1047, 1047, 1047, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 0, + 525, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 1047, + 1047, 341, 341, 341, 341, 341, 1151, 525, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1151, 1151, 1151, 1151, 1151, 1151, + 1155, 1155, 1151, 1151, 1155, 1155, 1151, 1151, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 1151, 341, 341, 341, + 341, 341, 341, 341, 341, 1151, 1155, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 1047, 1047, 1047, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 525, 341, 341, 341, 341, 341, 341, + 841, 841, 841, 341, 1272, 1151, 1272, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 543, 341, 543, 543, 556, 341, 341, + 543, 543, 341, 341, 341, 341, 341, 543, + 543, 341, 543, 341, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 341, 341, 525, 1047, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1155, 1151, 1151, 1155, + 1155, 1047, 1047, 341, 525, 525, 1155, 1163, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 341, 341, 341, 341, 341, 341, + 0, 0, 341, 341, 341, 341, 341, 341, + 0, 0, 341, 341, 341, 341, 341, 341, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 3998, 215, 215, 215, + 215, 215, 215, 215, 3941, 3999, 4000, 4001, + 4002, 215, 215, 215, 215, 215, 215, 215, + 215, 0, 0, 0, 0, 0, 0, 0, + 0, 4003, 4004, 4005, 4006, 4007, 4008, 4009, + 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, + 4018, 4019, 4020, 4021, 4022, 4023, 4024, 4025, + 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, + 4034, 4035, 4036, 4037, 4038, 4039, 4040, 4041, + 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, + 4050, 4051, 4052, 4053, 4054, 4055, 4056, 4057, + 4058, 4059, 4060, 4061, 4062, 4063, 4064, 4065, + 4066, 4067, 4068, 4069, 4070, 4071, 4072, 4073, + 4074, 4075, 4076, 4077, 4078, 4079, 4080, 4081, + 4082, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1155, 1155, 1151, 1155, + 1155, 1151, 1155, 1155, 1047, 1155, 1163, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4083, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4083, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 4084, 4084, 4084, + 4084, 4084, 4084, 4084, 4084, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, + 0, 0, 0, 0, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, + 1365, 1365, 1365, 1365, 1365, 0, 0, 0, + 0, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4085, 4085, 4085, 4085, 4085, 4085, 4085, + 4085, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, + 4094, 4094, 4095, 4096, 4097, 4098, 4099, 4100, + 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, + 4109, 4110, 4111, 4112, 4113, 4114, 4115, 4116, + 4117, 4118, 4119, 4120, 4121, 4122, 4123, 4124, + 4125, 4126, 4127, 4128, 4129, 4130, 4131, 4132, + 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, + 4141, 4142, 4143, 4144, 4145, 4146, 4147, 4148, + 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, + 4157, 4158, 4159, 4160, 4161, 4162, 4163, 4164, + 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, + 4173, 4174, 4175, 4176, 4177, 4106, 4178, 4179, + 4180, 4181, 4182, 4183, 4184, 4185, 4186, 4187, + 4188, 4189, 4190, 4191, 4192, 4193, 4194, 4195, + 4196, 4197, 4198, 4199, 4200, 4201, 4202, 4203, + 4204, 4205, 4206, 4207, 4208, 4209, 4210, 4211, + 4212, 4213, 4214, 4215, 4216, 4217, 4218, 4219, + 4220, 4221, 4222, 4223, 4224, 4225, 4226, 4227, + 4228, 4229, 4230, 4231, 4232, 4233, 4234, 4235, + 4236, 4237, 4238, 4239, 4240, 4241, 4242, 4243, + 4244, 4245, 4196, 4246, 4247, 4248, 4249, 4250, + 4251, 4252, 4253, 4180, 4254, 4255, 4256, 4257, + 4258, 4259, 4260, 4261, 4262, 4263, 4264, 4265, + 4266, 4267, 4268, 4269, 4270, 4271, 4272, 4273, + 4106, 4274, 4275, 4276, 4277, 4278, 4279, 4280, + 4281, 4282, 4283, 4284, 4285, 4286, 4287, 4288, + 4289, 4290, 4291, 4292, 4293, 4294, 4295, 4296, + 4297, 4298, 4299, 4300, 4182, 4301, 4302, 4303, + 4304, 4305, 4306, 4307, 4308, 4309, 4310, 4311, + 4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319, + 4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327, + 4328, 4329, 4330, 4331, 4332, 4333, 4334, 4335, + 4336, 4337, 4338, 4339, 4340, 4341, 4342, 4343, + 4344, 4345, 4346, 4347, 4348, 4349, 4350, 3035, + 3035, 4351, 3035, 4352, 3035, 3035, 4353, 4354, + 4355, 4356, 4357, 4358, 4359, 4360, 4361, 4362, + 3035, 4363, 3035, 4364, 3035, 3035, 4365, 4366, + 3035, 3035, 3035, 4367, 4368, 4369, 4370, 4371, + 4372, 4373, 4374, 4375, 4376, 4377, 4378, 4379, + 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387, + 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4395, + 4396, 4397, 4398, 4399, 4400, 4401, 4402, 4403, + 4404, 4405, 4406, 4407, 4408, 4409, 4410, 4411, + 4235, 4412, 4413, 4414, 4415, 4416, 4417, 4417, + 4418, 4419, 4420, 4421, 4422, 4423, 4424, 4425, + 4365, 4426, 4427, 4428, 4429, 4430, 4431, 0, + 0, 4432, 4433, 4434, 4435, 4436, 4437, 4438, + 4439, 4379, 4440, 4441, 4442, 4351, 4443, 4444, + 4445, 4446, 4447, 4448, 4449, 4450, 4451, 4452, + 4453, 4454, 4388, 4455, 4389, 4456, 4457, 4458, + 4459, 4460, 4352, 4127, 4461, 4462, 4463, 4197, + 4284, 4464, 4465, 4396, 4466, 4397, 4467, 4468, + 4469, 4354, 4470, 4471, 4472, 4473, 4474, 4355, + 4475, 4476, 4477, 4478, 4479, 4480, 4411, 4481, + 4482, 4235, 4483, 4415, 4484, 4485, 4486, 4487, + 4488, 4420, 4489, 4364, 4490, 4421, 4178, 4491, + 4422, 4492, 4424, 4493, 4494, 4495, 4496, 4497, + 4426, 4360, 4498, 4427, 4499, 4428, 4500, 4094, + 4501, 4502, 4503, 4504, 4505, 4506, 4507, 4508, + 4509, 4510, 4511, 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, 4512, 4513, 4514, 4515, 4516, 4517, 4518, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4519, 4520, 4521, 4522, + 4523, 0, 0, 0, 0, 0, 4524, 4525, + 4526, 4527, 4528, 4529, 4530, 4531, 4532, 4533, + 4534, 4535, 4536, 4537, 4538, 4539, 4540, 4541, + 4542, 4543, 4544, 4545, 4546, 4547, 4548, 4549, + 0, 4550, 4551, 4552, 4553, 4554, 0, 4555, + 0, 4556, 4557, 0, 4558, 4559, 0, 4560, + 4561, 4562, 4563, 4564, 4565, 4566, 4567, 4568, + 4569, 4570, 4571, 4572, 4573, 4574, 4575, 4576, + 4577, 4578, 4579, 4580, 4581, 4582, 4583, 4584, + 4585, 4586, 4587, 4588, 4589, 4590, 4591, 4592, + 4593, 4594, 4595, 4596, 4597, 4598, 4599, 4600, + 4601, 4602, 4603, 4604, 4605, 4606, 4607, 4608, + 4609, 4610, 4611, 4612, 4613, 4614, 4615, 4616, + 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, + 4625, 4626, 4627, 4628, 4629, 4630, 4631, 4632, + 4633, 4634, 4635, 4636, 4637, 4638, 4639, 4640, + 4641, 4642, 4643, 4644, 4645, 4646, 4647, 4648, + 4649, 4650, 4651, 4652, 4653, 4654, 4655, 4656, + 4657, 4658, 4659, 4660, 4661, 4662, 4663, 4664, + 4665, 4666, 4667, 4668, 4668, 4668, 4668, 4668, + 4668, 4668, 4668, 4668, 4668, 4668, 4668, 4668, + 4668, 4668, 4668, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4669, 4670, 4671, 4672, + 4673, 4674, 4675, 4676, 4677, 4678, 4679, 4680, + 4681, 4682, 4683, 4684, 4685, 4686, 4687, 4688, + 4689, 4690, 4691, 4692, 4693, 4694, 4695, 4696, + 4697, 4698, 4699, 4700, 4701, 4702, 4703, 4704, + 4705, 4706, 4707, 4708, 4709, 4710, 4711, 4712, + 4713, 4714, 4715, 4716, 4707, 4717, 4718, 4719, + 4720, 4721, 4722, 4723, 4724, 4725, 4726, 4727, + 4728, 4729, 4730, 4731, 4732, 4733, 4734, 4735, + 4736, 4737, 4738, 4739, 4740, 4741, 4742, 4743, + 4744, 4745, 4746, 4747, 4748, 4749, 4750, 4751, + 4752, 4753, 4754, 4755, 4756, 4757, 4758, 4759, + 4760, 4761, 4762, 4763, 4764, 4765, 4766, 4767, + 4768, 4769, 4770, 4771, 4772, 4773, 4774, 4775, + 4776, 4777, 4778, 4779, 4780, 4781, 4782, 4783, + 4784, 4785, 4786, 4787, 4788, 4789, 4790, 4791, + 4792, 4793, 4794, 4795, 4796, 4797, 4798, 4799, + 4800, 4801, 4802, 4803, 4804, 4805, 4806, 4807, + 4808, 4809, 4810, 4811, 4812, 4813, 4814, 4815, + 4816, 4708, 4817, 4818, 4819, 4820, 4821, 4822, + 4823, 4824, 4825, 4826, 4827, 4828, 4829, 4830, + 4831, 4832, 4833, 4834, 4835, 4836, 4837, 4838, + 4839, 4840, 4841, 4842, 4843, 4844, 4845, 4846, + 4847, 4848, 4849, 4850, 4851, 4852, 4853, 4854, + 4855, 4856, 4857, 4858, 4859, 4860, 4861, 4862, + 4863, 4864, 4865, 4866, 4867, 4868, 4869, 4870, + 4871, 4872, 4873, 4874, 4875, 4876, 4877, 4878, + 4879, 4880, 4881, 4882, 4883, 4884, 4885, 4886, + 4887, 4888, 4889, 4890, 4891, 4892, 4893, 4894, + 4895, 4896, 4897, 4898, 4899, 4900, 4901, 4902, + 4903, 4904, 4905, 4906, 4907, 4908, 4909, 4910, + 4911, 4912, 4913, 4914, 4915, 4916, 4917, 4918, + 4919, 4920, 4921, 4922, 4923, 4924, 4925, 4926, + 4927, 4928, 4929, 4930, 4931, 4932, 4933, 4934, + 4935, 4936, 4937, 4938, 4939, 4940, 4941, 4942, + 4943, 4944, 4945, 4946, 4947, 4948, 4949, 4950, + 4951, 4952, 4953, 4954, 4955, 4956, 4957, 4958, + 4959, 4960, 4961, 4962, 4963, 4964, 4965, 4966, + 4967, 4968, 4969, 4970, 4971, 4972, 4973, 4974, + 4975, 4976, 4977, 4978, 4979, 4980, 4981, 4982, + 4983, 4984, 4985, 4986, 4987, 4988, 4989, 4990, + 4991, 4992, 4993, 4994, 4995, 4996, 4997, 4998, + 4999, 5000, 5001, 5002, 5003, 5004, 5005, 5006, + 5007, 5008, 5009, 5010, 5011, 5012, 5013, 5014, + 5015, 5016, 5017, 5018, 5019, 5020, 5021, 5022, + 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, + 2137, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5031, 5032, 5033, 5034, 5035, 5036, 5037, + 5038, 5039, 5040, 5041, 5042, 5043, 5044, 5045, + 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, + 5054, 5055, 5056, 5057, 5058, 5059, 5060, 5061, + 5062, 5063, 5064, 5065, 5066, 5067, 5068, 5069, + 5070, 5071, 5072, 5073, 5074, 5075, 5076, 5077, + 5078, 5079, 5080, 5081, 5082, 5083, 5084, 5085, + 5086, 5087, 5088, 5089, 5090, 5091, 5092, 5093, + 5094, 0, 0, 5095, 5096, 5097, 5098, 5099, + 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, + 5108, 5109, 5110, 5111, 5112, 5113, 5114, 5115, + 5116, 5117, 5118, 5119, 5120, 5121, 5122, 5123, + 5124, 5125, 5126, 5127, 5128, 5129, 5130, 5131, + 5132, 5133, 5134, 5135, 5136, 5137, 5138, 5139, + 5140, 5141, 5142, 5143, 5144, 5145, 5146, 5147, + 5148, 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, 5149, 5150, 5151, 5152, 5153, 5154, 5155, + 5156, 5157, 5158, 5159, 5160, 5161, 77, 0, + 0, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 5162, 5163, 5164, 5165, 5166, 5167, 5168, + 5169, 5170, 5171, 0, 0, 0, 0, 0, + 0, 543, 543, 543, 543, 543, 543, 543, + 556, 556, 556, 556, 556, 556, 556, 543, + 543, 5172, 5173, 5174, 5175, 5175, 5176, 5177, + 5178, 5179, 5180, 5181, 5182, 5183, 5184, 5185, + 5186, 5187, 5188, 5189, 5190, 5191, 3033, 3033, + 5192, 5193, 5194, 5194, 5194, 5194, 5195, 5195, + 5195, 5196, 5197, 5198, 0, 5199, 5200, 5201, + 5202, 5203, 5204, 5205, 5206, 5207, 5208, 5209, + 5210, 5211, 5212, 5213, 5214, 5215, 5216, 5217, + 0, 5218, 5219, 5220, 5221, 0, 0, 0, + 0, 5222, 5223, 5224, 1117, 5225, 0, 5226, + 5227, 5228, 5229, 5230, 5231, 5232, 5233, 5234, + 5235, 5236, 5237, 5238, 5239, 5240, 5241, 5242, + 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, + 5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, + 5259, 5260, 5261, 5262, 5263, 5264, 5265, 5266, + 5267, 5268, 5269, 5270, 5271, 5272, 5273, 5274, + 5275, 5276, 5277, 5278, 5279, 5280, 5281, 5282, + 5283, 5284, 5285, 5286, 5287, 5288, 5289, 5290, + 5291, 5292, 5293, 5294, 5295, 5296, 5297, 5298, + 5299, 5300, 5301, 5302, 5303, 5304, 5305, 5306, + 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, + 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5322, + 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, + 5331, 5332, 5333, 5334, 5335, 5336, 5337, 5338, + 5339, 5340, 5341, 5342, 5343, 5344, 5345, 5346, + 5347, 5348, 5349, 5350, 5351, 5352, 5353, 5354, + 5355, 5356, 5357, 5358, 5359, 5360, 0, 0, + 1459, 0, 5361, 5362, 5363, 5364, 5365, 5366, + 5367, 5368, 5369, 5370, 5371, 5372, 5373, 5374, + 5375, 5376, 5377, 5378, 5379, 5380, 5381, 5382, + 5383, 5384, 5385, 5386, 5387, 5388, 5389, 5390, + 5391, 5392, 5393, 5394, 5395, 5396, 5397, 5398, + 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, + 5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, + 5415, 5416, 5417, 5418, 5419, 5420, 5421, 5422, + 5423, 5424, 5425, 5426, 5427, 5428, 5429, 5430, + 5431, 5432, 5433, 5434, 5435, 5436, 5437, 5438, + 5439, 5440, 5441, 5442, 5443, 5444, 5445, 5446, + 5447, 5448, 5449, 5450, 5451, 5452, 5453, 5454, + 5455, 5456, 5457, 5458, 5459, 5460, 5461, 5462, + 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, + 5471, 5472, 5473, 5474, 5475, 5476, 5477, 5478, + 5479, 5480, 5481, 5482, 5483, 5484, 5485, 5486, + 5487, 5488, 5489, 5490, 5491, 5492, 5493, 5494, + 5495, 5496, 5497, 5498, 5499, 5500, 5501, 5502, + 5503, 5504, 5505, 5506, 5507, 5508, 5509, 5510, + 5511, 5512, 5513, 5514, 5515, 5516, 5517, 5518, + 5519, 5520, 5521, 5522, 5523, 5524, 5525, 5526, + 5527, 5528, 5529, 5530, 5531, 5532, 5533, 5534, + 5535, 5536, 5537, 5538, 5539, 5540, 5541, 5542, + 5543, 5544, 5545, 5546, 5547, 5548, 5549, 5550, + 0, 0, 0, 5551, 5552, 5553, 5554, 5555, + 5556, 0, 0, 5557, 5558, 5559, 5560, 5561, + 5562, 0, 0, 5563, 5564, 5565, 5566, 5567, + 5568, 0, 0, 5569, 5570, 5571, 0, 0, + 0, 5572, 5573, 5574, 5575, 5576, 5577, 5578, + 0, 5579, 5580, 5581, 5582, 5583, 5584, 5585, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5586, 5586, 5586, 77, 77, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 0, 341, 341, 0, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 0, 0, 0, 0, + 0, 1047, 9, 1047, 0, 0, 0, 0, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 0, 0, 0, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 5587, 5587, 5587, 5587, 5587, 5587, 5587, + 5587, 5587, 5587, 5587, 5587, 5587, 5587, 5587, + 5587, 5587, 5587, 5587, 5587, 5587, 5587, 5587, + 5587, 5587, 5587, 5587, 5587, 5587, 5587, 5587, + 5587, 5587, 5587, 5587, 5587, 5587, 5587, 5587, + 5587, 5587, 5587, 5587, 5587, 5587, 5587, 5587, + 5587, 5587, 5587, 5587, 5587, 5587, 1211, 1211, + 1211, 1211, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 1211, 1211, 77, 841, 841, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 0, 0, 0, + 0, 77, 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, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 556, 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, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 556, 5588, 5588, 5588, 5588, 5588, 5588, + 5588, 5588, 5588, 5588, 5588, 5588, 5588, 5588, + 5588, 5588, 5588, 5588, 5588, 5588, 5588, 5588, + 5588, 5588, 5588, 5588, 5588, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 1182, 1182, 1182, 1182, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1458, 341, 341, 341, 341, 341, + 341, 341, 341, 1458, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 543, + 543, 543, 543, 543, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 1047, 1458, 1458, 1458, 1458, 1458, 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, 5589, 5590, 5591, 5592, 5593, 5594, 5595, + 5596, 5597, 5598, 5599, 5600, 5601, 5602, 5603, + 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, + 5612, 5613, 5614, 5615, 5616, 5617, 5618, 5619, + 5620, 5621, 5622, 5623, 5624, 5625, 5626, 5627, + 5628, 5629, 5630, 5631, 5632, 5633, 5634, 5635, + 5636, 5637, 5638, 5639, 5640, 5641, 5642, 5643, + 5644, 5645, 5646, 5647, 5648, 5649, 5650, 5651, + 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5659, + 5660, 5661, 5662, 5663, 5664, 5665, 5666, 5667, + 5668, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 5669, 5670, 5671, 5672, 5673, 5674, 5675, + 5676, 5677, 5678, 5679, 5680, 5681, 5682, 5683, + 5684, 5685, 5686, 5687, 5688, 5689, 5690, 5691, + 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, + 5700, 5701, 5702, 5703, 5704, 0, 0, 0, + 0, 5705, 5706, 5707, 5708, 5709, 5710, 5711, + 5712, 5713, 5714, 5715, 5716, 5717, 5718, 5719, + 5720, 5721, 5722, 5723, 5724, 5725, 5726, 5727, + 5728, 5729, 5730, 5731, 5732, 5733, 5734, 5735, + 5736, 5737, 5738, 5739, 5740, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1047, 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, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, + 0, 1108, 1108, 1108, 1108, 1108, 1108, 0, + 0, 1108, 0, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 0, + 1108, 1108, 0, 0, 0, 1108, 0, 0, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 0, + 1105, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 5742, 5742, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 0, 0, 0, 0, 0, 0, 0, 0, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 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, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 0, 1108, 1108, 0, + 0, 0, 0, 0, 5741, 5741, 5741, 5741, + 5741, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 5741, + 5741, 5741, 5741, 5741, 5741, 0, 0, 0, + 9, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 0, 0, 0, 0, 0, + 1105, 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, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 0, 0, 0, 0, 5741, 5741, 1108, + 1108, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 0, 0, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 1108, 1151, 1151, 1151, 0, 1151, 1151, + 0, 0, 0, 0, 0, 1151, 556, 1151, + 543, 1108, 1108, 1108, 1108, 0, 1108, 1108, + 1108, 0, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 0, + 0, 543, 569, 556, 0, 0, 0, 0, + 1163, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 5741, 0, 0, 0, 0, 0, 0, + 0, 1105, 1105, 1105, 1105, 1105, 1105, 1105, + 1105, 1105, 0, 0, 0, 0, 0, 0, + 0, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 5741, 5741, + 1105, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 5741, 5741, + 5741, 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, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 5742, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 543, 556, + 0, 0, 0, 0, 5741, 5741, 5741, 5741, + 5741, 1105, 1105, 1105, 1105, 1105, 1105, 1105, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 0, + 0, 0, 9, 9, 9, 9, 9, 9, + 9, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 0, + 0, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 0, 0, 0, 0, + 0, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 0, 0, 0, 0, 0, + 0, 0, 1105, 1105, 1105, 1105, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 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, 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, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 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, 0, 0, 0, 0, 0, 0, + 0, 5743, 5744, 5745, 5746, 5747, 5748, 5749, + 5750, 5751, 5752, 5753, 5754, 5755, 5756, 5757, + 5758, 5759, 5760, 5761, 5762, 5763, 5764, 5765, + 5766, 5767, 5768, 5769, 5770, 5771, 5772, 5773, + 5774, 5775, 5776, 5777, 5778, 5779, 5780, 5781, + 5782, 5783, 5784, 5785, 5786, 5787, 5788, 5789, + 5790, 5791, 5792, 5793, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5794, 5795, 5796, 5797, 5798, 5799, 5800, + 5801, 5802, 5803, 5804, 5805, 5806, 5807, 5808, + 5809, 5810, 5811, 5812, 5813, 5814, 5815, 5816, + 5817, 5818, 5819, 5820, 5821, 5822, 5823, 5824, + 5825, 5826, 5827, 5828, 5829, 5830, 5831, 5832, + 5833, 5834, 5835, 5836, 5837, 5838, 5839, 5840, + 5841, 5842, 5843, 5844, 0, 0, 0, 0, + 0, 0, 0, 5741, 5741, 5741, 5741, 5741, + 5741, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 543, 543, 543, + 543, 0, 0, 0, 0, 0, 0, 0, + 0, 1135, 1135, 1135, 1135, 1135, 1135, 1135, + 1135, 1135, 1135, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, + 0, 5845, 5845, 5845, 5845, 5845, 5845, 5845, + 5845, 5845, 5845, 5845, 5845, 5845, 5845, 5845, + 5845, 5845, 5845, 5845, 5845, 5845, 5845, 5845, + 5845, 5845, 5845, 5845, 5845, 5845, 5845, 5845, + 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, + 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, + 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, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 5741, 5741, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 1108, 0, 0, 0, 0, 0, 0, 0, + 0, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 1117, + 1117, 1117, 1117, 1117, 1117, 1117, 1117, 556, + 556, 543, 543, 543, 556, 543, 556, 556, + 556, 556, 5846, 5846, 5846, 5846, 1112, 1112, + 1112, 1112, 1112, 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, 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, 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, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1155, 1151, 1155, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1163, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 0, + 0, 0, 0, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1211, + 1211, 1211, 1211, 1211, 1211, 1211, 1211, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1163, 1151, 1151, 1155, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 5847, 5848, 5849, 5850, 341, 341, + 341, 341, 341, 341, 341, 341, 5851, 341, + 341, 341, 341, 341, 5852, 341, 341, 341, + 341, 1155, 1155, 1155, 1151, 1151, 1151, 1151, + 1155, 1155, 1163, 5853, 1047, 1047, 5854, 1047, + 1047, 1047, 1047, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5854, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 543, 543, 543, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 5855, 1151, 1151, 1151, 1151, 1155, 1151, 5856, + 5857, 1151, 5858, 5859, 1163, 1163, 0, 1172, + 1172, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1047, 1047, 1047, 1047, 341, 1155, 1155, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1173, 1047, 1047, 341, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1151, 1151, 1155, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1155, 1155, 1155, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1155, 1483, 341, 1228, 1228, 341, 1047, 1047, + 1047, 1047, 1151, 1173, 1151, 1151, 1047, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 341, 1047, 341, 1047, 1047, + 1047, 0, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 1155, 1155, 1155, + 1151, 1151, 1151, 1155, 1155, 1151, 1483, 1173, + 1151, 1047, 1047, 1047, 1047, 1047, 1047, 1151, + 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, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 0, 341, 341, 341, 341, 0, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1047, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 1151, 1155, 1155, 1155, 1151, 1151, 1151, 1151, + 1151, 1151, 1173, 1163, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 1151, 1151, 1155, 1155, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 0, + 341, 341, 0, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 0, 341, 341, + 341, 341, 341, 0, 1173, 1173, 341, 5860, + 1155, 1151, 1155, 1155, 1155, 1155, 0, 0, + 5861, 1155, 0, 0, 5862, 5863, 1483, 0, + 0, 341, 0, 0, 0, 0, 0, 0, + 5864, 0, 0, 0, 0, 0, 341, 341, + 341, 341, 341, 1155, 1155, 0, 0, 543, + 543, 543, 543, 543, 543, 543, 0, 0, + 0, 543, 543, 543, 543, 543, 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, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 1155, 1155, + 1155, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1155, 1155, 1163, 1151, 1151, 1155, 1173, + 341, 341, 341, 341, 1047, 1047, 1047, 1047, + 1047, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 1047, 0, 1047, 543, + 341, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 5865, 1155, 1155, 1151, 1151, 1151, 1151, + 1151, 1151, 5866, 5867, 5868, 5869, 5870, 5871, + 1151, 1151, 1155, 1163, 1173, 341, 341, 1047, + 341, 0, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 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, 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, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 5872, 1155, 1155, 1151, 1151, 1151, 1151, 0, + 0, 5873, 5874, 5875, 5876, 1151, 1151, 1155, + 1163, 1173, 1047, 1047, 1047, 1047, 1047, 1047, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, + 1047, 341, 341, 341, 341, 1151, 1151, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 1155, 1155, 1155, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1155, 1155, 1151, 1155, + 1163, 1151, 1047, 1047, 1047, 341, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1151, 1155, 1151, 1155, + 1155, 1151, 1151, 1151, 1151, 1151, 1151, 1483, + 1173, 341, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 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, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 0, 0, 1151, 1151, + 1151, 1155, 1155, 1151, 1151, 1151, 1151, 1155, + 1151, 1151, 1151, 1151, 1163, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1182, 1182, 1047, 1047, 1047, + 841, 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, 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, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 1155, 1155, 1155, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1155, 1163, 1173, 1047, 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, 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, 0, 0, 0, + 0, 5877, 5878, 5879, 5880, 5881, 5882, 5883, + 5884, 5885, 5886, 5887, 5888, 5889, 5890, 5891, + 5892, 5893, 5894, 5895, 5896, 5897, 5898, 5899, + 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, + 5908, 5909, 5910, 5911, 5912, 5913, 5914, 5915, + 5916, 5917, 5918, 5919, 5920, 5921, 5922, 5923, + 5924, 5925, 5926, 5927, 5928, 5929, 5930, 5931, + 5932, 5933, 5934, 5935, 5936, 5937, 5938, 5939, + 5940, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 341, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 0, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1155, 1155, 1155, 1151, 1151, 1151, + 1151, 0, 0, 1151, 1151, 1155, 1155, 1155, + 1155, 1163, 341, 1047, 341, 1155, 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, 341, 1151, 1151, 1151, 1151, 1151, 1151, + 5941, 5941, 1151, 1151, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1151, 1163, 1151, 1151, + 1151, 1151, 1155, 1228, 1151, 1151, 1151, 1151, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, + 1163, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 1151, 1151, 1151, 1151, 1151, 1151, + 1155, 1155, 1151, 1151, 1151, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 1228, 1228, 1228, + 1228, 1228, 1228, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1155, 1151, 1163, 1047, 1047, 1047, 341, 1047, + 1047, 1047, 1047, 1047, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 1155, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 0, 1151, 1151, 1151, 1151, 1151, 1151, 1155, + 5942, 341, 1047, 1047, 1047, 1047, 1047, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 0, 0, + 0, 1047, 1047, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 0, 0, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 0, 1155, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1155, 1151, 1151, 1155, 1151, 1151, + 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, + 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, 341, 341, 341, 341, 341, 341, 341, + 0, 341, 341, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 1151, 1151, 1151, 1151, 1151, 1151, + 0, 0, 0, 1151, 0, 1151, 1151, 0, + 1151, 1151, 1151, 1173, 1151, 1163, 1163, 1228, + 1151, 0, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 0, + 341, 341, 0, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 1155, 1155, 1155, 1155, 1155, + 0, 1151, 1151, 0, 1155, 1155, 1151, 1155, + 1163, 341, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 1151, 1151, 1155, 1155, + 1047, 1047, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, + 0, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 77, 77, + 77, 77, 77, 77, 77, 77, 11, 11, + 11, 11, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1047, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 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, 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, 0, 0, 0, 0, 0, + 0, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, + 0, 1047, 1047, 1047, 1047, 1047, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 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, 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, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 5943, 5943, 5943, 5943, 5943, 5943, 5943, + 5943, 5943, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 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, + 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, + 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, + 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 1047, + 1047, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 0, + 0, 569, 569, 569, 569, 569, 1047, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 543, 543, 543, 543, 543, 543, 543, + 1047, 1047, 1047, 1047, 1047, 841, 841, 841, + 841, 525, 525, 525, 525, 1047, 841, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 0, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 0, 0, 0, 0, 0, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 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, 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, 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, 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, 5944, 5945, 5946, 5947, 5948, 5949, 5950, + 5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, + 5959, 5960, 5961, 5962, 5963, 5964, 5965, 5966, + 5967, 5968, 5969, 5970, 5971, 5972, 5973, 5974, + 5975, 5976, 5977, 5978, 5979, 5980, 5981, 5982, + 5983, 5984, 5985, 5986, 5987, 5988, 5989, 5990, + 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, + 5999, 6000, 6001, 6002, 6003, 6004, 6005, 6006, + 6007, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1047, 1047, 1047, 1047, 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, 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, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 0, 0, 0, 0, + 1151, 341, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, + 1155, 0, 0, 0, 0, 0, 0, 0, + 1151, 1151, 1151, 1151, 525, 525, 525, 525, + 525, 525, 525, 525, 525, 525, 525, 525, + 525, 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, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3034, 3034, 3033, 3034, 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, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 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, + 0, 3035, 3035, 3035, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3035, 3035, 3035, + 3035, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 0, 0, 841, 1151, 569, + 1047, 1459, 1459, 1459, 1459, 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, 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, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 0, 0, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 6008, 6009, 841, 841, 841, 841, 841, 6010, + 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, + 569, 569, 569, 841, 841, 841, 6019, 6020, + 6021, 6022, 6023, 6024, 1459, 1459, 1459, 1459, + 1459, 1459, 1459, 1459, 556, 556, 556, 556, + 556, 556, 556, 556, 841, 841, 543, 543, + 543, 543, 543, 556, 556, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 543, 543, 543, 543, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 6025, 6026, 6027, 6028, 6029, 6030, + 6031, 6032, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 543, 543, 543, 77, 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, 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, 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 1182, 1182, 1182, 1182, 1182, 1182, + 1182, 1182, 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, 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, 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, 6033, 2233, 2208, 2251, 2235, 2236, 6034, + 2215, 2218, 6035, 6036, 2219, 2238, 2221, 6037, + 2223, 2224, 2225, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 2229, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6033, 2233, 2208, + 2251, 2235, 2236, 6034, 2215, 2218, 6035, 6036, + 2219, 2238, 2221, 6037, 2223, 2224, 2225, 6038, + 6039, 6040, 6041, 6042, 6043, 6044, 2229, 6045, + 6046, 6047, 2252, 2234, 6048, 2214, 0, 2253, + 2254, 6049, 2220, 6050, 6051, 2239, 6052, 6053, + 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, + 6062, 6033, 2233, 2208, 2251, 2235, 2236, 6034, + 2215, 2218, 6035, 6036, 2219, 2238, 2221, 6037, + 2223, 2224, 2225, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 2229, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6033, 0, 2208, + 2251, 0, 0, 6034, 0, 0, 6035, 6036, + 0, 0, 2221, 6037, 2223, 2224, 0, 6038, + 6039, 6040, 6041, 6042, 6043, 6044, 2229, 6045, + 6046, 6047, 2252, 0, 6048, 0, 2216, 2253, + 2254, 6049, 2220, 6050, 6051, 0, 6052, 6053, + 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, + 6062, 6033, 2233, 2208, 2251, 2235, 2236, 6034, + 2215, 2218, 6035, 6036, 2219, 2238, 2221, 6037, + 2223, 2224, 2225, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 2229, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6033, 2233, 0, + 2251, 2235, 2236, 6034, 0, 0, 6035, 6036, + 2219, 2238, 2221, 6037, 2223, 2224, 0, 6038, + 6039, 6040, 6041, 6042, 6043, 6044, 0, 6045, + 6046, 6047, 2252, 2234, 6048, 2214, 2216, 2253, + 2254, 6049, 2220, 6050, 6051, 2239, 6052, 6053, + 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, + 6062, 6033, 2233, 0, 2251, 2235, 2236, 6034, + 0, 2218, 6035, 6036, 2219, 2238, 0, 6037, + 0, 0, 0, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 0, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6033, 2233, 2208, + 2251, 2235, 2236, 6034, 2215, 2218, 6035, 6036, + 2219, 2238, 2221, 6037, 2223, 2224, 2225, 6038, + 6039, 6040, 6041, 6042, 6043, 6044, 2229, 6045, + 6046, 6047, 2252, 2234, 6048, 2214, 2216, 2253, + 2254, 6049, 2220, 6050, 6051, 2239, 6052, 6053, + 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, + 6062, 6033, 2233, 2208, 2251, 2235, 2236, 6034, + 2215, 2218, 6035, 6036, 2219, 2238, 2221, 6037, + 2223, 2224, 2225, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 2229, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6033, 2233, 2208, + 2251, 2235, 2236, 6034, 2215, 2218, 6035, 6036, + 2219, 2238, 2221, 6037, 2223, 2224, 2225, 6038, + 6039, 6040, 6041, 6042, 6043, 6044, 2229, 6045, + 6046, 6047, 2252, 2234, 6048, 2214, 2216, 2253, + 2254, 6049, 2220, 6050, 6051, 2239, 6052, 6053, + 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, + 6062, 6033, 2233, 2208, 2251, 2235, 2236, 6034, + 2215, 2218, 6035, 6036, 2219, 2238, 2221, 6037, + 2223, 2224, 2225, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 2229, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6033, 2233, 2208, + 2251, 2235, 2236, 6034, 2215, 2218, 6035, 6036, + 2219, 2238, 2221, 6037, 2223, 2224, 2225, 6038, + 6039, 6040, 6041, 6042, 6043, 6044, 2229, 6045, + 6046, 6047, 2252, 2234, 6048, 2214, 2216, 2253, + 2254, 6049, 2220, 6050, 6051, 2239, 6052, 6053, + 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, + 6062, 6033, 2233, 2208, 2251, 2235, 2236, 6034, + 2215, 2218, 6035, 6036, 2219, 2238, 2221, 6037, + 2223, 2224, 2225, 6038, 6039, 6040, 6041, 6042, + 6043, 6044, 2229, 6045, 6046, 6047, 2252, 2234, + 6048, 2214, 2216, 2253, 2254, 6049, 2220, 6050, + 6051, 2239, 6052, 6053, 6054, 6055, 6056, 6057, + 6058, 6059, 6060, 6061, 6062, 6063, 6064, 0, + 0, 6065, 6066, 2248, 6067, 6068, 6069, 6070, + 6071, 6072, 6073, 6074, 6075, 6076, 6077, 6078, + 2249, 6079, 6080, 6081, 6082, 6083, 6084, 6085, + 6086, 6087, 6088, 6089, 6090, 2247, 6091, 6092, + 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6100, + 6101, 6102, 2246, 6103, 6104, 6105, 6106, 6107, + 6108, 6109, 6110, 6111, 6112, 6113, 6114, 6115, + 6116, 6117, 6118, 6065, 6066, 2248, 6067, 6068, + 6069, 6070, 6071, 6072, 6073, 6074, 6075, 6076, + 6077, 6078, 2249, 6079, 6080, 6081, 6082, 6083, + 6084, 6085, 6086, 6087, 6088, 6089, 6090, 2247, + 6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, + 6099, 6100, 6101, 6102, 2246, 6103, 6104, 6105, + 6106, 6107, 6108, 6109, 6110, 6111, 6112, 6113, + 6114, 6115, 6116, 6117, 6118, 6065, 6066, 2248, + 6067, 6068, 6069, 6070, 6071, 6072, 6073, 6074, + 6075, 6076, 6077, 6078, 2249, 6079, 6080, 6081, + 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, + 6090, 2247, 6091, 6092, 6093, 6094, 6095, 6096, + 6097, 6098, 6099, 6100, 6101, 6102, 2246, 6103, + 6104, 6105, 6106, 6107, 6108, 6109, 6110, 6111, + 6112, 6113, 6114, 6115, 6116, 6117, 6118, 6065, + 6066, 2248, 6067, 6068, 6069, 6070, 6071, 6072, + 6073, 6074, 6075, 6076, 6077, 6078, 2249, 6079, + 6080, 6081, 6082, 6083, 6084, 6085, 6086, 6087, + 6088, 6089, 6090, 2247, 6091, 6092, 6093, 6094, + 6095, 6096, 6097, 6098, 6099, 6100, 6101, 6102, + 2246, 6103, 6104, 6105, 6106, 6107, 6108, 6109, + 6110, 6111, 6112, 6113, 6114, 6115, 6116, 6117, + 6118, 6065, 6066, 2248, 6067, 6068, 6069, 6070, + 6071, 6072, 6073, 6074, 6075, 6076, 6077, 6078, + 2249, 6079, 6080, 6081, 6082, 6083, 6084, 6085, + 6086, 6087, 6088, 6089, 6090, 2247, 6091, 6092, + 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6100, + 6101, 6102, 2246, 6103, 6104, 6105, 6106, 6107, + 6108, 6109, 6110, 6111, 6112, 6113, 6114, 6115, + 6116, 6117, 6118, 6119, 6120, 0, 0, 6121, + 6122, 6123, 6124, 6125, 6126, 6127, 6128, 6129, + 6130, 6121, 6122, 6123, 6124, 6125, 6126, 6127, + 6128, 6129, 6130, 6121, 6122, 6123, 6124, 6125, + 6126, 6127, 6128, 6129, 6130, 6121, 6122, 6123, + 6124, 6125, 6126, 6127, 6128, 6129, 6130, 6121, + 6122, 6123, 6124, 6125, 6126, 6127, 6128, 6129, + 6130, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 841, 841, 841, 841, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 841, 841, + 841, 841, 841, 841, 841, 841, 1151, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 1151, 841, 841, + 1047, 1047, 1047, 1047, 1047, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1151, 1151, 1151, 1151, + 1151, 0, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, + 1151, 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, 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, 543, 543, 543, 543, 543, 543, 543, + 0, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 0, 0, 543, 543, 543, 543, + 543, 543, 543, 0, 543, 543, 0, 543, + 543, 543, 543, 543, 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, 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, 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, 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, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 0, 0, + 0, 543, 543, 543, 543, 543, 543, 543, + 525, 525, 525, 525, 525, 525, 525, 0, + 0, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 341, + 841, 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, 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, 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, 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, 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, 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, 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, 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, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 543, 543, 543, + 543, 1172, 1172, 1172, 1172, 1172, 1172, 1172, + 1172, 1172, 1172, 0, 0, 0, 0, 0, + 11, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 1108, 1108, + 1108, 1108, 1108, 1108, 1108, 1108, 0, 0, + 5741, 5741, 5741, 5741, 5741, 5741, 5741, 5741, + 5741, 556, 556, 556, 556, 556, 556, 556, + 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, 6131, 6132, 6133, 6134, 6135, 6136, 6137, + 6138, 6139, 6140, 6141, 6142, 6143, 6144, 6145, + 6146, 6147, 6148, 6149, 6150, 6151, 6152, 6153, + 6154, 6155, 6156, 6157, 6158, 6159, 6160, 6161, + 6162, 6163, 6164, 6165, 6166, 6167, 6168, 6169, + 6170, 6171, 6172, 6173, 6174, 6175, 6176, 6177, + 6178, 6179, 6180, 6181, 6182, 6183, 6184, 6185, + 6186, 6187, 6188, 6189, 6190, 6191, 6192, 6193, + 6194, 6195, 6196, 6197, 6198, 543, 543, 543, + 543, 543, 543, 1173, 1153, 0, 0, 0, + 0, 1152, 1152, 1152, 1152, 1152, 1152, 1152, + 1152, 1152, 1152, 0, 0, 0, 0, 1105, + 1105, 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, 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, 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, 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, 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, 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, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 1148, 5846, 5846, + 5846, 1111, 5846, 5846, 5846, 5846, 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, 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, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 1148, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 5846, + 5846, 5846, 5846, 5846, 5846, 5846, 5846, 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, 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, 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, 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, 0, + 0, 6199, 6200, 6201, 6202, 0, 6203, 6204, + 6205, 6206, 6207, 6208, 6209, 6210, 6211, 6212, + 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, + 6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, + 6229, 0, 6200, 6201, 0, 6230, 0, 0, + 6205, 0, 6207, 6208, 6209, 6210, 6211, 6212, + 6213, 6214, 6215, 6216, 0, 6218, 6219, 6220, + 6221, 0, 6223, 0, 6225, 0, 0, 0, + 0, 0, 0, 6201, 0, 0, 0, 0, + 6205, 0, 6207, 0, 6209, 0, 6211, 6212, + 6213, 0, 6215, 6216, 0, 6218, 0, 0, + 6221, 0, 6223, 0, 6225, 0, 6227, 0, + 6229, 0, 6200, 6201, 0, 6230, 0, 0, + 6205, 6206, 6207, 6208, 0, 6210, 6211, 6212, + 6213, 6214, 6215, 6216, 0, 6218, 6219, 6220, + 6221, 0, 6223, 6224, 6225, 6226, 0, 6228, + 0, 6199, 6200, 6201, 6202, 6230, 6203, 6204, + 6205, 6206, 6207, 0, 6209, 6210, 6211, 6212, + 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, + 6221, 6222, 6223, 6224, 6225, 0, 0, 0, + 0, 0, 6200, 6201, 6202, 0, 6203, 6204, + 6205, 6206, 6207, 0, 6209, 6210, 6211, 6212, + 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, + 6221, 6222, 6223, 6224, 6225, 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, 0, 0, 0, + 0, 75, 75, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 79, 79, 79, 79, 2397, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 0, 0, 0, + 0, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 0, 0, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 0, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 2397, 0, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6231, 6232, 6233, 6234, 6235, 6236, 6237, + 6238, 6239, 6240, 6241, 1211, 1211, 0, 0, + 0, 6242, 6243, 6244, 6245, 6246, 6247, 6248, + 6249, 6250, 6251, 6252, 6253, 6254, 6255, 6256, + 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, + 6265, 6266, 6267, 6268, 6269, 6270, 6271, 6272, + 79, 6273, 6274, 6275, 6276, 6277, 6278, 6279, + 6280, 6281, 6282, 6283, 6284, 6285, 6286, 6287, + 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, + 6296, 6297, 6298, 6299, 6300, 6301, 6302, 6303, + 6304, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 6305, 6306, 6307, 0, 0, + 0, 2541, 2541, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 2541, + 2541, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 6308, + 841, 6309, 6308, 6308, 6308, 6308, 6308, 6308, + 6308, 6308, 6308, 6308, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 841, 841, + 841, 841, 841, 841, 841, 841, 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, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6310, + 6310, 6310, 6310, 6310, 6310, 6310, 6310, 6310, + 6310, 6310, 6310, 6310, 6310, 6310, 6310, 6310, + 6310, 6310, 6310, 6310, 6310, 6310, 6310, 6310, + 6310, 6311, 6312, 6313, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6314, 6315, 6316, 6317, 6318, 6319, 6320, + 6321, 6322, 6323, 6324, 6325, 6326, 6327, 6328, + 6329, 6330, 6331, 6332, 6333, 6334, 6335, 6336, + 6337, 6338, 6339, 6340, 6341, 6342, 6343, 6344, + 6345, 6346, 6347, 6348, 6349, 6350, 6351, 6352, + 6353, 6354, 6355, 6356, 6357, 0, 0, 0, + 0, 6358, 6359, 6360, 6361, 6362, 6363, 6364, + 6365, 6366, 0, 0, 0, 0, 0, 0, + 0, 6367, 6368, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2397, 2397, 2397, 2397, 2397, 2397, 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, 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, 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 79, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 79, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 79, 79, 79, 79, + 2397, 2397, 2397, 2397, 2397, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 79, 79, 79, 2397, 79, 79, + 79, 2397, 2397, 2397, 6369, 6369, 6369, 6369, + 6369, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 79, 2397, 79, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 79, 79, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 77, + 77, 77, 77, 77, 77, 77, 77, 79, + 79, 79, 79, 79, 2397, 2397, 2397, 2397, + 79, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 2397, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 2397, 2397, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 2397, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 79, + 79, 79, 79, 79, 79, 2397, 79, 79, + 79, 2397, 2397, 2397, 79, 79, 2397, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 2397, 2397, 0, 0, + 0, 79, 79, 79, 79, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 79, 79, + 79, 79, 0, 0, 0, 0, 0, 0, + 0, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 0, 0, 0, 0, 0, 0, 0, + 0, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 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, 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, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 0, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2815, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2815, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 0, 2397, 2397, 2397, 2397, + 0, 0, 0, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 0, 0, 2397, 2397, + 2397, 2397, 2397, 2397, 0, 0, 0, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 0, 0, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, + 2397, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 0, + 0, 2397, 2397, 2397, 2397, 0, 0, 0, + 0, 2397, 2397, 2397, 0, 0, 0, 0, + 0, 2397, 2397, 2397, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2397, 2397, 2397, 2397, 2397, 2397, 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, 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, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 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, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 3035, 3035, 3035, 3035, 3035, 3035, + 3035, 3035, 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, 6370, 6371, 6372, 6373, 6374, 4373, 6375, + 6376, 6377, 6378, 4374, 6379, 6380, 6381, 4375, + 6382, 6383, 6384, 6385, 6386, 6387, 6388, 6389, + 6390, 6391, 6392, 6393, 4433, 6394, 6395, 6396, + 6397, 6398, 6399, 6400, 6401, 6402, 4438, 4376, + 4377, 4439, 6403, 6404, 4184, 6405, 4378, 6406, + 6407, 6408, 6409, 6409, 6409, 6410, 6411, 6412, + 6413, 6414, 6415, 6416, 6417, 6418, 6419, 6420, + 6421, 6422, 6423, 6424, 6425, 6426, 6427, 6427, + 4441, 6428, 6429, 6430, 6431, 4380, 6432, 6433, + 6434, 4337, 6435, 6436, 6437, 6438, 6439, 6440, + 6441, 6442, 6443, 6444, 6445, 6446, 6447, 6448, + 6449, 6450, 6451, 6452, 6453, 6454, 6455, 6456, + 6457, 6458, 6459, 6460, 6460, 6461, 6462, 6463, + 4180, 6464, 6465, 6466, 6467, 6468, 6469, 6470, + 6471, 4385, 6472, 6473, 6474, 6475, 6476, 6477, + 6478, 6479, 6480, 6481, 6482, 6483, 6484, 6485, + 6486, 6487, 6488, 6489, 6490, 6491, 6492, 4126, + 6493, 6494, 6495, 6495, 6496, 6497, 6497, 6498, + 6499, 6500, 6501, 6502, 6503, 6504, 6505, 6506, + 6507, 6508, 6509, 6510, 4386, 6511, 6512, 6513, + 6514, 4453, 6514, 6515, 4388, 6516, 6517, 6518, + 6519, 4389, 4099, 6520, 6521, 6522, 6523, 6524, + 6525, 6526, 6527, 6528, 6529, 6530, 6531, 6532, + 6533, 6534, 6535, 6536, 6537, 6538, 6539, 6540, + 6541, 4390, 6542, 6543, 6544, 6545, 6546, 6547, + 4392, 6548, 6549, 6550, 6551, 6552, 6553, 6554, + 6555, 4127, 4461, 6556, 6557, 6558, 6559, 6560, + 6561, 6562, 6563, 4393, 6564, 6565, 6566, 6567, + 4504, 6568, 6569, 6570, 6571, 6572, 6573, 6574, + 6575, 6576, 6577, 6578, 6579, 6580, 4197, 6581, + 6582, 6583, 6584, 6585, 6586, 6587, 6588, 6589, + 6590, 6591, 4394, 4284, 6592, 6593, 6594, 6595, + 6596, 6597, 6598, 6599, 4465, 6600, 6601, 6602, + 6603, 6604, 6605, 6606, 6607, 4466, 6608, 6609, + 6610, 6611, 6612, 6613, 6614, 6615, 6616, 6617, + 6618, 6619, 4468, 6620, 6621, 6622, 6623, 6624, + 6625, 6626, 6627, 6628, 6629, 6630, 6630, 6631, + 6632, 4470, 6633, 6634, 6635, 6636, 6637, 6638, + 6639, 4183, 6640, 6641, 6642, 6643, 6644, 6645, + 6646, 4476, 6647, 6648, 6649, 6650, 6651, 6652, + 6652, 4477, 4506, 6653, 6654, 6655, 6656, 6657, + 4145, 4479, 6658, 6659, 4405, 6660, 6661, 4359, + 6662, 6663, 4409, 6664, 6665, 6666, 6667, 6667, + 6668, 6669, 6670, 6671, 6672, 6673, 6674, 6675, + 6676, 6677, 6678, 6679, 6680, 6681, 6682, 6683, + 6684, 6685, 6686, 6687, 6688, 6689, 6690, 6691, + 6692, 6693, 6694, 4415, 6695, 6696, 6697, 6698, + 6699, 6700, 6701, 6702, 6703, 6704, 6705, 6706, + 6707, 6708, 6709, 6710, 6496, 6711, 6712, 6713, + 6714, 6715, 6716, 6717, 6718, 6719, 6720, 6721, + 6722, 4201, 6723, 6724, 6725, 6726, 6727, 6728, + 4418, 6729, 6730, 6731, 6732, 6733, 6734, 6735, + 6736, 6737, 6738, 6739, 6740, 6741, 6742, 6743, + 6744, 6745, 6746, 6747, 6748, 4140, 6749, 6750, + 6751, 6752, 6753, 6754, 4486, 6755, 6756, 6757, + 6758, 6759, 6760, 6761, 6762, 6763, 6764, 6765, + 6766, 6767, 6768, 6769, 6770, 6771, 6772, 6773, + 6774, 4491, 4492, 6775, 6776, 6777, 6778, 6779, + 6780, 6781, 6782, 6783, 6784, 6785, 6786, 6787, + 4493, 6788, 6789, 6790, 6791, 6792, 6793, 6794, + 6795, 6796, 6797, 6798, 6799, 6800, 6801, 6802, + 6803, 6804, 6805, 6806, 6807, 6808, 6809, 6810, + 6811, 6812, 6813, 6814, 6815, 6816, 6817, 4499, + 4499, 6818, 6819, 6820, 6821, 6822, 6823, 6824, + 6825, 6826, 6827, 4500, 6828, 6829, 6830, 6831, + 6832, 6833, 6834, 6835, 6836, 6837, 6838, 6839, + 6840, 6841, 6842, 6843, 6844, 6845, 6846, 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, 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, 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, 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, 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, 1459, 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, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 2129, 2129, 2129, 2129, 2129, 2129, 2129, + 2129, 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, 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, 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, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 577, 577, 577, 577, + 577, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 4086, + 4086, 4086, 4086, 4086, 4086, 4086, 4086, 0, + 0, }; + +static const utf8proc_property_t utf8proc_properties[] = { + {0, 0, 0, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false,false,false,false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_S, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_B, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_LF}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_WS, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_B, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CR}, + {UTF8PROC_CATEGORY_CC, 0, UTF8PROC_BIDI_CLASS_B, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ES, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5093, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5084, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5096, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 0, UINT16_MAX, 0, UINT16_MAX, 0, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1, UINT16_MAX, 1, UINT16_MAX, 2784, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 2, UINT16_MAX, 2, UINT16_MAX, 49, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3, UINT16_MAX, 3, UINT16_MAX, 704, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 4, UINT16_MAX, 4, UINT16_MAX, 62, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5, UINT16_MAX, 5, UINT16_MAX, 2872, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6, UINT16_MAX, 6, UINT16_MAX, 782, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7, UINT16_MAX, 7, UINT16_MAX, 808, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 8, UINT16_MAX, 8, UINT16_MAX, 111, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 9, UINT16_MAX, 9, UINT16_MAX, 898, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 10, UINT16_MAX, 10, UINT16_MAX, 913, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 11, UINT16_MAX, 11, UINT16_MAX, 999, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 12, UINT16_MAX, 12, UINT16_MAX, 2890, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 13, UINT16_MAX, 13, UINT16_MAX, 160, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 14, UINT16_MAX, 14, UINT16_MAX, 205, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 15, UINT16_MAX, 15, UINT16_MAX, 2982, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 16, UINT16_MAX, 16, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 17, UINT16_MAX, 17, UINT16_MAX, 1087, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 18, UINT16_MAX, 18, UINT16_MAX, 1173, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 19, UINT16_MAX, 19, UINT16_MAX, 1257, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 20, UINT16_MAX, 20, UINT16_MAX, 254, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 21, UINT16_MAX, 21, UINT16_MAX, 3042, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 22, UINT16_MAX, 22, UINT16_MAX, 1337, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 23, UINT16_MAX, 23, UINT16_MAX, 3122, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 24, UINT16_MAX, 24, UINT16_MAX, 303, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 25, UINT16_MAX, 25, UINT16_MAX, 1423, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1491, UINT16_MAX, 1491, 352, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1493, UINT16_MAX, 1493, 2818, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2812, UINT16_MAX, 2812, 401, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1494, UINT16_MAX, 1494, 743, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1495, UINT16_MAX, 1495, 414, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2837, UINT16_MAX, 2837, 2875, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1497, UINT16_MAX, 1497, 795, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1498, UINT16_MAX, 1498, 853, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1499, UINT16_MAX, 1499, 463, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1500, UINT16_MAX, 1500, 901, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1501, UINT16_MAX, 1501, 956, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1502, UINT16_MAX, 1502, 1043, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1503, UINT16_MAX, 1503, 2932, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1504, UINT16_MAX, 1504, 512, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1505, UINT16_MAX, 1505, 557, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1507, UINT16_MAX, 1507, 2994, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2826, UINT16_MAX, 2826, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1508, UINT16_MAX, 1508, 1130, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 3305, UINT16_MAX, 3305, 1215, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1509, UINT16_MAX, 1509, 1296, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1510, UINT16_MAX, 1510, 606, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2908, UINT16_MAX, 2908, 3082, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1511, UINT16_MAX, 1511, 1380, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2925, UINT16_MAX, 2925, 3131, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 3312, UINT16_MAX, 3312, 655, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2834, UINT16_MAX, 2834, 1466, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_NOBREAK, 26, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8219, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 1621, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PI, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 1, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8221, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ET, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ET, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 31, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 32, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8225, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 35, 35, 7220, UINT16_MAX, 7220, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8228, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 38, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 14, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PF, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 16423, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 16426, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 16429, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8240, 50, UINT16_MAX, 50, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8243, 53, UINT16_MAX, 53, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8246, 56, UINT16_MAX, 56, UINT16_MAX, 3143, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8249, 59, UINT16_MAX, 59, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8252, 62, UINT16_MAX, 62, UINT16_MAX, 1537, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8255, 65, UINT16_MAX, 65, UINT16_MAX, 1579, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 66, UINT16_MAX, 66, UINT16_MAX, 1549, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8259, 69, UINT16_MAX, 69, UINT16_MAX, 2852, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8262, 72, UINT16_MAX, 72, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8265, 75, UINT16_MAX, 75, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8268, 78, UINT16_MAX, 78, UINT16_MAX, 3357, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8271, 81, UINT16_MAX, 81, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8274, 84, UINT16_MAX, 84, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8277, 87, UINT16_MAX, 87, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8280, 90, UINT16_MAX, 90, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8283, 93, UINT16_MAX, 93, UINT16_MAX, 2878, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 94, UINT16_MAX, 94, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8287, 97, UINT16_MAX, 97, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8290, 100, UINT16_MAX, 100, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8293, 103, UINT16_MAX, 103, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8296, 106, UINT16_MAX, 106, UINT16_MAX, 3461, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8299, 109, UINT16_MAX, 109, UINT16_MAX, 1597, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8302, 112, UINT16_MAX, 112, UINT16_MAX, 1591, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 113, UINT16_MAX, 113, UINT16_MAX, 1585, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8306, 116, UINT16_MAX, 116, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8309, 119, UINT16_MAX, 119, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8312, 122, UINT16_MAX, 122, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8315, 125, UINT16_MAX, 125, UINT16_MAX, 1509, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8318, 128, UINT16_MAX, 128, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 129, UINT16_MAX, 129, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 8322, 8069, UINT16_MAX, 8069, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8324, UINT16_MAX, 8070, UINT16_MAX, 8070, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8326, UINT16_MAX, 8071, UINT16_MAX, 8071, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8328, UINT16_MAX, 8072, UINT16_MAX, 8072, 3192, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8330, UINT16_MAX, 8073, UINT16_MAX, 8073, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8332, UINT16_MAX, 8074, UINT16_MAX, 8074, 1540, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8334, UINT16_MAX, 2836, UINT16_MAX, 2836, 1582, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1492, UINT16_MAX, 1492, 1558, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8336, UINT16_MAX, 8075, UINT16_MAX, 8075, 2855, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8338, UINT16_MAX, 8076, UINT16_MAX, 8076, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8340, UINT16_MAX, 8077, UINT16_MAX, 8077, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8342, UINT16_MAX, 8078, UINT16_MAX, 8078, 3406, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8344, UINT16_MAX, 8079, UINT16_MAX, 8079, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8346, UINT16_MAX, 8080, UINT16_MAX, 8080, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8348, UINT16_MAX, 8081, UINT16_MAX, 8081, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8350, UINT16_MAX, 8082, UINT16_MAX, 8082, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8352, UINT16_MAX, 8083, UINT16_MAX, 8083, 2881, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8084, UINT16_MAX, 8084, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8354, UINT16_MAX, 8085, UINT16_MAX, 8085, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8356, UINT16_MAX, 8086, UINT16_MAX, 8086, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8358, UINT16_MAX, 8087, UINT16_MAX, 8087, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8360, UINT16_MAX, 8088, UINT16_MAX, 8088, 3510, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8362, UINT16_MAX, 8089, UINT16_MAX, 8089, 1606, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8364, UINT16_MAX, 8090, UINT16_MAX, 8090, 1594, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8091, UINT16_MAX, 8091, 1588, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8366, UINT16_MAX, 8092, UINT16_MAX, 8092, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8368, UINT16_MAX, 8093, UINT16_MAX, 8093, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8370, UINT16_MAX, 8094, UINT16_MAX, 8094, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8372, UINT16_MAX, 8095, UINT16_MAX, 8095, 1523, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8374, UINT16_MAX, 8096, UINT16_MAX, 8096, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8097, UINT16_MAX, 8097, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8376, UINT16_MAX, 8098, UINT16_MAX, 8098, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8378, 188, UINT16_MAX, 188, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8381, UINT16_MAX, 8099, UINT16_MAX, 8099, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8383, 193, UINT16_MAX, 193, UINT16_MAX, 3259, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8386, UINT16_MAX, 8100, UINT16_MAX, 8100, 3308, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8388, 198, UINT16_MAX, 198, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8391, UINT16_MAX, 8101, UINT16_MAX, 8101, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8393, 203, UINT16_MAX, 203, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8396, UINT16_MAX, 8102, UINT16_MAX, 8102, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8398, 208, UINT16_MAX, 208, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8401, UINT16_MAX, 8103, UINT16_MAX, 8103, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8403, 213, UINT16_MAX, 213, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8406, UINT16_MAX, 8104, UINT16_MAX, 8104, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8408, 218, UINT16_MAX, 218, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8411, UINT16_MAX, 8105, UINT16_MAX, 8105, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8413, 223, UINT16_MAX, 223, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8416, UINT16_MAX, 8106, UINT16_MAX, 8106, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 226, UINT16_MAX, 226, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8107, UINT16_MAX, 8107, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8419, 229, UINT16_MAX, 229, UINT16_MAX, 2858, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8422, UINT16_MAX, 8108, UINT16_MAX, 8108, 2862, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8424, 234, UINT16_MAX, 234, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8427, UINT16_MAX, 8109, UINT16_MAX, 8109, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8429, 239, UINT16_MAX, 239, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8432, UINT16_MAX, 8110, UINT16_MAX, 8110, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8434, 244, UINT16_MAX, 244, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8437, UINT16_MAX, 8111, UINT16_MAX, 8111, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8439, 249, UINT16_MAX, 249, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8442, UINT16_MAX, 8112, UINT16_MAX, 8112, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8444, 254, UINT16_MAX, 254, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8447, UINT16_MAX, 8113, UINT16_MAX, 8113, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8449, 259, UINT16_MAX, 259, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8452, UINT16_MAX, 8114, UINT16_MAX, 8114, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8454, 264, UINT16_MAX, 264, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8457, UINT16_MAX, 8115, UINT16_MAX, 8115, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8459, 269, UINT16_MAX, 269, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8462, UINT16_MAX, 8116, UINT16_MAX, 8116, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8464, 274, UINT16_MAX, 274, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8467, UINT16_MAX, 8117, UINT16_MAX, 8117, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 277, UINT16_MAX, 277, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 5164, UINT16_MAX, 5164, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8470, 280, UINT16_MAX, 280, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8473, UINT16_MAX, 8118, UINT16_MAX, 8118, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8475, 285, UINT16_MAX, 285, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8478, UINT16_MAX, 8119, UINT16_MAX, 8119, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8480, 290, UINT16_MAX, 290, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8483, UINT16_MAX, 8120, UINT16_MAX, 8120, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8485, 295, UINT16_MAX, 295, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8488, UINT16_MAX, 8121, UINT16_MAX, 8121, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8490, 8492, UINT16_MAX, 8, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1499, UINT16_MAX, 1499, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8494, 304, UINT16_MAX, 304, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8497, UINT16_MAX, 8122, UINT16_MAX, 8122, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8499, 309, UINT16_MAX, 309, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8502, UINT16_MAX, 8123, UINT16_MAX, 8123, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8504, 314, UINT16_MAX, 314, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8507, UINT16_MAX, 8124, UINT16_MAX, 8124, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8509, 319, UINT16_MAX, 319, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8512, UINT16_MAX, 8125, UINT16_MAX, 8125, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8514, 324, UINT16_MAX, 324, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8517, UINT16_MAX, 8126, UINT16_MAX, 8126, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8519, 329, UINT16_MAX, 329, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8522, UINT16_MAX, 8127, UINT16_MAX, 8127, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8524, 334, UINT16_MAX, 334, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8527, UINT16_MAX, 8128, UINT16_MAX, 8128, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 337, UINT16_MAX, 337, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8129, UINT16_MAX, 8129, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8530, 340, UINT16_MAX, 340, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8533, UINT16_MAX, 8130, UINT16_MAX, 8130, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8535, 345, UINT16_MAX, 345, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8538, UINT16_MAX, 8131, UINT16_MAX, 8131, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8540, 350, UINT16_MAX, 350, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8543, UINT16_MAX, 8132, UINT16_MAX, 8132, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8545, 8545, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 355, UINT16_MAX, 355, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8133, UINT16_MAX, 8133, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8548, 358, UINT16_MAX, 358, UINT16_MAX, 2974, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8551, UINT16_MAX, 8134, UINT16_MAX, 8134, 2978, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8553, 363, UINT16_MAX, 363, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8556, UINT16_MAX, 8135, UINT16_MAX, 8135, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8558, 368, UINT16_MAX, 368, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8561, UINT16_MAX, 8136, UINT16_MAX, 8136, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 371, UINT16_MAX, 371, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8137, UINT16_MAX, 8137, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8564, 374, UINT16_MAX, 374, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8567, UINT16_MAX, 8138, UINT16_MAX, 8138, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8569, 379, UINT16_MAX, 379, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8572, UINT16_MAX, 8139, UINT16_MAX, 8139, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8574, 384, UINT16_MAX, 384, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8577, UINT16_MAX, 8140, UINT16_MAX, 8140, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8579, 389, UINT16_MAX, 389, UINT16_MAX, 3012, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8582, UINT16_MAX, 8141, UINT16_MAX, 8141, 3015, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8584, 394, UINT16_MAX, 394, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8587, UINT16_MAX, 8142, UINT16_MAX, 8142, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8589, 399, UINT16_MAX, 399, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8592, UINT16_MAX, 8143, UINT16_MAX, 8143, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8594, 404, UINT16_MAX, 404, UINT16_MAX, 3018, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8597, UINT16_MAX, 8144, UINT16_MAX, 8144, 3021, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8599, 409, UINT16_MAX, 409, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8602, UINT16_MAX, 8145, UINT16_MAX, 8145, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8604, 414, UINT16_MAX, 414, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8607, UINT16_MAX, 8146, UINT16_MAX, 8146, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 417, UINT16_MAX, 417, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8147, UINT16_MAX, 8147, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8610, 420, UINT16_MAX, 420, UINT16_MAX, 3030, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8613, UINT16_MAX, 8148, UINT16_MAX, 8148, 3033, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8615, 425, UINT16_MAX, 425, UINT16_MAX, 3036, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8618, UINT16_MAX, 8149, UINT16_MAX, 8149, 3039, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8620, 430, UINT16_MAX, 430, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8623, UINT16_MAX, 8150, UINT16_MAX, 8150, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8625, 435, UINT16_MAX, 435, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8628, UINT16_MAX, 8151, UINT16_MAX, 8151, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8630, 440, UINT16_MAX, 440, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8633, UINT16_MAX, 8152, UINT16_MAX, 8152, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8635, 445, UINT16_MAX, 445, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8638, UINT16_MAX, 8153, UINT16_MAX, 8153, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8640, 450, UINT16_MAX, 450, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8643, UINT16_MAX, 8154, UINT16_MAX, 8154, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8645, 455, UINT16_MAX, 455, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8648, UINT16_MAX, 8155, UINT16_MAX, 8155, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8650, 460, UINT16_MAX, 460, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8653, 463, UINT16_MAX, 463, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8656, UINT16_MAX, 8156, UINT16_MAX, 8156, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8658, 468, UINT16_MAX, 468, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8661, UINT16_MAX, 8157, UINT16_MAX, 8157, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8663, 473, UINT16_MAX, 473, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8666, UINT16_MAX, 8158, UINT16_MAX, 8158, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 18, 18, 3305, UINT16_MAX, 3305, 3140, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8159, UINT16_MAX, 8159, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 476, UINT16_MAX, 476, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 477, UINT16_MAX, 477, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8160, UINT16_MAX, 8160, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 478, UINT16_MAX, 478, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8161, UINT16_MAX, 8161, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 479, UINT16_MAX, 479, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 480, UINT16_MAX, 480, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8162, UINT16_MAX, 8162, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 481, UINT16_MAX, 481, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 482, UINT16_MAX, 482, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 483, UINT16_MAX, 483, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8163, UINT16_MAX, 8163, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 484, UINT16_MAX, 484, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 485, UINT16_MAX, 485, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 486, UINT16_MAX, 486, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 487, UINT16_MAX, 487, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8164, UINT16_MAX, 8164, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 488, UINT16_MAX, 488, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 489, UINT16_MAX, 489, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8165, UINT16_MAX, 8165, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 490, UINT16_MAX, 490, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 491, UINT16_MAX, 491, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 492, UINT16_MAX, 492, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8166, UINT16_MAX, 8166, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8167, UINT16_MAX, 8167, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 493, UINT16_MAX, 493, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 494, UINT16_MAX, 494, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8168, UINT16_MAX, 8168, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 495, UINT16_MAX, 495, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8688, 498, UINT16_MAX, 498, UINT16_MAX, 3565, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8691, UINT16_MAX, 8169, UINT16_MAX, 8169, 3614, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 501, UINT16_MAX, 501, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8170, UINT16_MAX, 8170, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 502, UINT16_MAX, 502, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8171, UINT16_MAX, 8171, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 503, UINT16_MAX, 503, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 504, UINT16_MAX, 504, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8172, UINT16_MAX, 8172, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 505, UINT16_MAX, 505, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 506, UINT16_MAX, 506, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8173, UINT16_MAX, 8173, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 507, UINT16_MAX, 507, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8700, 510, UINT16_MAX, 510, UINT16_MAX, 3663, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8703, UINT16_MAX, 8174, UINT16_MAX, 8174, 3712, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 513, UINT16_MAX, 513, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 514, UINT16_MAX, 514, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 515, UINT16_MAX, 515, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8175, UINT16_MAX, 8175, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 516, UINT16_MAX, 516, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8176, UINT16_MAX, 8176, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 517, UINT16_MAX, 517, UINT16_MAX, 1573, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 518, UINT16_MAX, 518, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8177, UINT16_MAX, 8177, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 519, UINT16_MAX, 519, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8178, UINT16_MAX, 8178, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8179, UINT16_MAX, 8179, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8712, 522, UINT16_MAX, 522, 8180, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8715, 522, 8181, 522, 8180, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8717, UINT16_MAX, 8181, UINT16_MAX, 8180, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8719, 529, UINT16_MAX, 529, 8182, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8722, 529, 8183, 529, 8182, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8724, UINT16_MAX, 8183, UINT16_MAX, 8182, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8726, 536, UINT16_MAX, 536, 8184, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8729, 536, 8185, 536, 8184, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8731, UINT16_MAX, 8185, UINT16_MAX, 8184, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8733, 543, UINT16_MAX, 543, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8736, UINT16_MAX, 8186, UINT16_MAX, 8186, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8738, 548, UINT16_MAX, 548, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8741, UINT16_MAX, 8187, UINT16_MAX, 8187, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8743, 553, UINT16_MAX, 553, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8746, UINT16_MAX, 8188, UINT16_MAX, 8188, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8748, 558, UINT16_MAX, 558, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8751, UINT16_MAX, 8189, UINT16_MAX, 8189, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8753, 563, UINT16_MAX, 563, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8756, UINT16_MAX, 8190, UINT16_MAX, 8190, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8758, 568, UINT16_MAX, 568, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8761, UINT16_MAX, 8191, UINT16_MAX, 8191, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8763, 573, UINT16_MAX, 573, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8766, UINT16_MAX, 8192, UINT16_MAX, 8192, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8768, 578, UINT16_MAX, 578, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8771, UINT16_MAX, 8193, UINT16_MAX, 8193, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1496, UINT16_MAX, 1496, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8773, 583, UINT16_MAX, 583, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8776, UINT16_MAX, 8194, UINT16_MAX, 8194, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8778, 588, UINT16_MAX, 588, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8781, UINT16_MAX, 8195, UINT16_MAX, 8195, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8783, 593, UINT16_MAX, 593, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8786, UINT16_MAX, 8196, UINT16_MAX, 8196, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 596, UINT16_MAX, 596, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8197, UINT16_MAX, 8197, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8789, 599, UINT16_MAX, 599, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8792, UINT16_MAX, 8198, UINT16_MAX, 8198, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8794, 604, UINT16_MAX, 604, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8797, UINT16_MAX, 8199, UINT16_MAX, 8199, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8799, 609, UINT16_MAX, 609, UINT16_MAX, 1567, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8802, UINT16_MAX, 8200, UINT16_MAX, 8200, 1570, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8804, 614, UINT16_MAX, 614, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8807, UINT16_MAX, 8201, UINT16_MAX, 8201, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8809, 619, UINT16_MAX, 619, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8812, UINT16_MAX, 8202, UINT16_MAX, 8202, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8814, 8814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8816, 626, UINT16_MAX, 626, 8203, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8819, 626, 8204, 626, 8203, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8821, UINT16_MAX, 8204, UINT16_MAX, 8203, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8823, 633, UINT16_MAX, 633, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8826, UINT16_MAX, 8205, UINT16_MAX, 8205, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 636, UINT16_MAX, 636, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 637, UINT16_MAX, 637, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8830, 640, UINT16_MAX, 640, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8833, UINT16_MAX, 8206, UINT16_MAX, 8206, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8835, 645, UINT16_MAX, 645, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8838, UINT16_MAX, 8207, UINT16_MAX, 8207, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8840, 650, UINT16_MAX, 650, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8843, UINT16_MAX, 8208, UINT16_MAX, 8208, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8845, 655, UINT16_MAX, 655, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8848, UINT16_MAX, 8209, UINT16_MAX, 8209, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8850, 660, UINT16_MAX, 660, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8853, UINT16_MAX, 8210, UINT16_MAX, 8210, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8855, 665, UINT16_MAX, 665, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8858, UINT16_MAX, 8211, UINT16_MAX, 8211, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8860, 670, UINT16_MAX, 670, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8863, UINT16_MAX, 8212, UINT16_MAX, 8212, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8865, 675, UINT16_MAX, 675, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8868, UINT16_MAX, 8213, UINT16_MAX, 8213, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8870, 680, UINT16_MAX, 680, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8873, UINT16_MAX, 8214, UINT16_MAX, 8214, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8875, 685, UINT16_MAX, 685, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8878, UINT16_MAX, 8215, UINT16_MAX, 8215, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8880, 690, UINT16_MAX, 690, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8883, UINT16_MAX, 8216, UINT16_MAX, 8216, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8885, 695, UINT16_MAX, 695, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8888, UINT16_MAX, 8217, UINT16_MAX, 8217, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8890, 700, UINT16_MAX, 700, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8893, UINT16_MAX, 8218, UINT16_MAX, 8218, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8895, 705, UINT16_MAX, 705, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8898, UINT16_MAX, 8219, UINT16_MAX, 8219, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8900, 710, UINT16_MAX, 710, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8903, UINT16_MAX, 8220, UINT16_MAX, 8220, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8905, 715, UINT16_MAX, 715, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8908, UINT16_MAX, 8221, UINT16_MAX, 8221, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8910, 720, UINT16_MAX, 720, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8913, UINT16_MAX, 8222, UINT16_MAX, 8222, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8915, 725, UINT16_MAX, 725, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8918, UINT16_MAX, 8223, UINT16_MAX, 8223, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 728, UINT16_MAX, 728, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8224, UINT16_MAX, 8224, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8921, 731, UINT16_MAX, 731, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8924, UINT16_MAX, 8225, UINT16_MAX, 8225, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 734, UINT16_MAX, 734, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 735, UINT16_MAX, 735, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 1506, UINT16_MAX, 1506, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 736, UINT16_MAX, 736, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8226, UINT16_MAX, 8226, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8929, 739, UINT16_MAX, 739, UINT16_MAX, 1543, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8932, UINT16_MAX, 8227, UINT16_MAX, 8227, 1546, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8934, 744, UINT16_MAX, 744, UINT16_MAX, 2866, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8937, UINT16_MAX, 8228, UINT16_MAX, 8228, 2869, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8939, 749, UINT16_MAX, 749, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8942, UINT16_MAX, 8229, UINT16_MAX, 8229, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8944, 754, UINT16_MAX, 754, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8947, UINT16_MAX, 8230, UINT16_MAX, 8230, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8949, 759, UINT16_MAX, 759, UINT16_MAX, 1615, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8952, UINT16_MAX, 8231, UINT16_MAX, 8231, 1618, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8954, 764, UINT16_MAX, 764, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8957, UINT16_MAX, 8232, UINT16_MAX, 8232, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 8959, 769, UINT16_MAX, 769, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 8962, UINT16_MAX, 8233, UINT16_MAX, 8233, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 772, UINT16_MAX, 772, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 773, UINT16_MAX, 773, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8234, UINT16_MAX, 8234, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 774, UINT16_MAX, 774, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 775, UINT16_MAX, 775, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8235, UINT16_MAX, 8235, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8236, UINT16_MAX, 8236, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 776, UINT16_MAX, 776, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8237, UINT16_MAX, 8237, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 777, UINT16_MAX, 777, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 778, UINT16_MAX, 778, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 779, UINT16_MAX, 779, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 780, UINT16_MAX, 780, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8238, UINT16_MAX, 8238, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 781, UINT16_MAX, 781, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8239, UINT16_MAX, 8239, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 782, UINT16_MAX, 782, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8240, UINT16_MAX, 8240, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 783, UINT16_MAX, 783, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8241, UINT16_MAX, 8241, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 784, UINT16_MAX, 784, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8242, UINT16_MAX, 8242, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8243, UINT16_MAX, 8243, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8244, UINT16_MAX, 8244, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8245, UINT16_MAX, 8245, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8246, UINT16_MAX, 8246, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8247, UINT16_MAX, 8247, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8248, UINT16_MAX, 8248, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8249, UINT16_MAX, 8249, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8250, UINT16_MAX, 8250, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2821, UINT16_MAX, 2821, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8251, UINT16_MAX, 8251, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8252, UINT16_MAX, 8252, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8253, UINT16_MAX, 8253, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8254, UINT16_MAX, 8254, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8255, UINT16_MAX, 8255, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8256, UINT16_MAX, 8256, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8257, UINT16_MAX, 8257, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8258, UINT16_MAX, 8258, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8259, UINT16_MAX, 8259, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8260, UINT16_MAX, 8260, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8261, UINT16_MAX, 8261, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8262, UINT16_MAX, 8262, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8263, UINT16_MAX, 8263, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8264, UINT16_MAX, 8264, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8265, UINT16_MAX, 8265, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8266, UINT16_MAX, 8266, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8267, UINT16_MAX, 8267, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8268, UINT16_MAX, 8268, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8269, UINT16_MAX, 8269, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8270, UINT16_MAX, 8270, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8271, UINT16_MAX, 8271, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8272, UINT16_MAX, 8272, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8273, UINT16_MAX, 8273, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8274, UINT16_MAX, 8274, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8275, UINT16_MAX, 8275, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8276, UINT16_MAX, 8276, 1576, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8277, UINT16_MAX, 8277, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8278, UINT16_MAX, 8278, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 7, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 9, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 17, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 788, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 22, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 24, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8981, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8983, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8985, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8987, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8989, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 8991, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 489, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 11, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 18, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 23, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32768, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32769, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32770, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32771, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32775, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32776, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32778, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32772, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32814, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32773, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32780, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32779, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32782, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32783, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32815, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32816, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 232, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 216, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32781, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 202, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32808, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32813, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32807, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32784, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 202, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32774, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 202, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32777, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32810, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32812, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32811, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32809, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 1, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 1, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32819, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, 802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, 803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32817, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, 804, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, 8997, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 240, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, 807, 7217, UINT16_MAX, 7217, 32818, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 233, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 234, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 808, UINT16_MAX, 808, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8279, UINT16_MAX, 8279, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 809, UINT16_MAX, 809, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8280, UINT16_MAX, 8280, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 810, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 811, UINT16_MAX, 811, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8281, UINT16_MAX, 8281, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9004, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8282, UINT16_MAX, 8282, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8283, UINT16_MAX, 8283, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8284, UINT16_MAX, 8284, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, 814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 815, UINT16_MAX, 815, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 9008, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9010, 820, UINT16_MAX, 820, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, 821, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9014, 824, UINT16_MAX, 824, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9017, 827, UINT16_MAX, 827, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9020, 830, UINT16_MAX, 830, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9023, 833, UINT16_MAX, 833, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9026, 836, UINT16_MAX, 836, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9029, 839, UINT16_MAX, 839, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9032, 17226, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 845, UINT16_MAX, 845, UINT16_MAX, 1673, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 846, UINT16_MAX, 846, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 847, UINT16_MAX, 847, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 848, UINT16_MAX, 848, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 849, UINT16_MAX, 849, UINT16_MAX, 1726, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 850, UINT16_MAX, 850, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 851, UINT16_MAX, 851, UINT16_MAX, 1777, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 852, UINT16_MAX, 852, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 807, UINT16_MAX, 807, UINT16_MAX, 1830, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 853, UINT16_MAX, 853, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 854, UINT16_MAX, 854, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 35, UINT16_MAX, 35, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 855, UINT16_MAX, 855, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 856, UINT16_MAX, 856, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 857, UINT16_MAX, 857, UINT16_MAX, 1881, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 858, UINT16_MAX, 858, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 859, UINT16_MAX, 859, UINT16_MAX, 5027, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 860, UINT16_MAX, 860, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 861, UINT16_MAX, 861, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 862, UINT16_MAX, 862, UINT16_MAX, 1932, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 863, UINT16_MAX, 863, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 864, UINT16_MAX, 864, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 865, UINT16_MAX, 865, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 866, UINT16_MAX, 866, UINT16_MAX, 1983, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9059, 869, UINT16_MAX, 869, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9062, 872, UINT16_MAX, 872, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9065, UINT16_MAX, 2603, UINT16_MAX, 2603, 4904, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9067, UINT16_MAX, 2635, UINT16_MAX, 2635, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9069, UINT16_MAX, 2640, UINT16_MAX, 2640, 4913, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9071, UINT16_MAX, 2676, UINT16_MAX, 2676, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9073, 17267, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7211, UINT16_MAX, 7211, 2088, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7212, UINT16_MAX, 7212, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2846, UINT16_MAX, 2846, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7213, UINT16_MAX, 7213, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7214, UINT16_MAX, 7214, 2141, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7215, UINT16_MAX, 7215, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7216, UINT16_MAX, 7216, 2192, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 915, UINT16_MAX, 915, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7217, UINT16_MAX, 7217, 2245, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7218, UINT16_MAX, 7218, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7219, UINT16_MAX, 7219, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7220, UINT16_MAX, 7220, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7221, UINT16_MAX, 7221, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7222, UINT16_MAX, 7222, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7223, UINT16_MAX, 7223, 2401, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2847, UINT16_MAX, 2847, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7224, UINT16_MAX, 7224, 5023, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 860, 917, UINT16_MAX, 917, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 917, UINT16_MAX, 917, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7226, UINT16_MAX, 7226, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 897, UINT16_MAX, 897, 2349, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7227, UINT16_MAX, 7227, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7228, UINT16_MAX, 7228, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7229, UINT16_MAX, 7229, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 2835, UINT16_MAX, 2835, 2452, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9078, UINT16_MAX, 8285, UINT16_MAX, 8285, 2036, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9080, UINT16_MAX, 8286, UINT16_MAX, 8286, 2297, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9082, UINT16_MAX, 2745, UINT16_MAX, 2745, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9084, UINT16_MAX, 2714, UINT16_MAX, 2714, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9086, UINT16_MAX, 2750, UINT16_MAX, 2750, 5033, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 896, UINT16_MAX, 896, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 846, 846, 7212, UINT16_MAX, 7212, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 852, 852, 915, UINT16_MAX, 915, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 897, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2505, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9090, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9092, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 863, 863, 7227, UINT16_MAX, 7227, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 858, 858, 2847, UINT16_MAX, 2847, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8287, UINT16_MAX, 8287, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 902, UINT16_MAX, 902, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8288, UINT16_MAX, 8288, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 903, UINT16_MAX, 903, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8289, UINT16_MAX, 8289, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 904, UINT16_MAX, 904, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 7238, UINT16_MAX, 7238, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 905, UINT16_MAX, 905, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8290, UINT16_MAX, 8290, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 906, UINT16_MAX, 906, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8291, UINT16_MAX, 8291, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 907, UINT16_MAX, 907, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8292, UINT16_MAX, 8292, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 908, UINT16_MAX, 908, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8293, UINT16_MAX, 8293, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 909, UINT16_MAX, 909, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8294, UINT16_MAX, 8294, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 910, UINT16_MAX, 910, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8295, UINT16_MAX, 8295, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 911, UINT16_MAX, 911, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8296, UINT16_MAX, 8296, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 912, UINT16_MAX, 912, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8297, UINT16_MAX, 8297, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 913, UINT16_MAX, 913, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8298, UINT16_MAX, 8298, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 853, 853, 7218, UINT16_MAX, 7218, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 859, 859, 7224, UINT16_MAX, 7224, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 914, UINT16_MAX, 8299, UINT16_MAX, 8299, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8300, UINT16_MAX, 8300, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 915, 852, UINT16_MAX, 852, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 849, 849, 7214, UINT16_MAX, 7214, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 916, UINT16_MAX, 916, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8301, UINT16_MAX, 8301, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 917, 918, UINT16_MAX, 918, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 919, UINT16_MAX, 919, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8302, UINT16_MAX, 8302, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 920, UINT16_MAX, 920, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 921, UINT16_MAX, 921, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 922, UINT16_MAX, 922, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9115, 925, UINT16_MAX, 925, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9118, 928, UINT16_MAX, 928, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 929, UINT16_MAX, 929, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9122, 932, UINT16_MAX, 932, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 933, UINT16_MAX, 933, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 934, UINT16_MAX, 934, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 935, UINT16_MAX, 935, UINT16_MAX, 2525, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9128, 938, UINT16_MAX, 938, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 939, UINT16_MAX, 939, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 940, UINT16_MAX, 940, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 941, UINT16_MAX, 941, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 942, UINT16_MAX, 942, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9135, 945, UINT16_MAX, 945, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9138, 948, UINT16_MAX, 948, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9141, 951, UINT16_MAX, 951, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 952, UINT16_MAX, 952, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 953, UINT16_MAX, 953, UINT16_MAX, 2615, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 954, UINT16_MAX, 954, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 955, UINT16_MAX, 955, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 956, UINT16_MAX, 956, UINT16_MAX, 2522, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 957, UINT16_MAX, 957, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 958, UINT16_MAX, 958, UINT16_MAX, 2511, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 959, UINT16_MAX, 959, UINT16_MAX, 2601, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 960, UINT16_MAX, 960, UINT16_MAX, 2635, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 961, UINT16_MAX, 961, UINT16_MAX, 2531, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9154, 964, UINT16_MAX, 964, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 965, UINT16_MAX, 965, UINT16_MAX, 2528, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 966, UINT16_MAX, 966, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 967, UINT16_MAX, 967, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 968, UINT16_MAX, 968, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 969, UINT16_MAX, 969, UINT16_MAX, 2641, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 970, UINT16_MAX, 970, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 971, UINT16_MAX, 971, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 972, UINT16_MAX, 972, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 973, UINT16_MAX, 973, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 974, UINT16_MAX, 974, UINT16_MAX, 2542, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 975, UINT16_MAX, 975, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 976, UINT16_MAX, 976, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 977, UINT16_MAX, 977, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 978, UINT16_MAX, 978, UINT16_MAX, 2659, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 979, UINT16_MAX, 979, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 980, UINT16_MAX, 980, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 981, UINT16_MAX, 981, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 982, UINT16_MAX, 982, UINT16_MAX, 2665, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 983, UINT16_MAX, 983, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 984, UINT16_MAX, 984, UINT16_MAX, 2653, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 985, UINT16_MAX, 985, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 986, UINT16_MAX, 986, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8303, UINT16_MAX, 8303, 2622, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8304, UINT16_MAX, 8304, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8305, UINT16_MAX, 8305, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8306, UINT16_MAX, 8306, 2575, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8307, UINT16_MAX, 8307, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8308, UINT16_MAX, 8308, 2564, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8309, UINT16_MAX, 8309, 2608, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8310, UINT16_MAX, 8310, 2638, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8311, UINT16_MAX, 8311, 2553, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9179, UINT16_MAX, 8312, UINT16_MAX, 8312, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8313, UINT16_MAX, 8313, 2581, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8314, UINT16_MAX, 8314, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8315, UINT16_MAX, 8315, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8316, UINT16_MAX, 8316, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8317, UINT16_MAX, 8317, 2644, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8318, UINT16_MAX, 8318, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8319, UINT16_MAX, 8319, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8320, UINT16_MAX, 8320, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8321, UINT16_MAX, 8321, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8322, UINT16_MAX, 8322, 2584, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8323, UINT16_MAX, 8323, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8324, UINT16_MAX, 8324, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8325, UINT16_MAX, 8325, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8326, UINT16_MAX, 8326, 2662, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8327, UINT16_MAX, 8327, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8328, UINT16_MAX, 8328, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8329, UINT16_MAX, 8329, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8330, UINT16_MAX, 8330, 2668, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8331, UINT16_MAX, 8331, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8332, UINT16_MAX, 8332, 2656, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8333, UINT16_MAX, 8333, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8334, UINT16_MAX, 8334, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9181, UINT16_MAX, 8335, UINT16_MAX, 8335, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9183, UINT16_MAX, 8336, UINT16_MAX, 8336, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8337, UINT16_MAX, 8337, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9185, UINT16_MAX, 8338, UINT16_MAX, 8338, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8339, UINT16_MAX, 8339, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8340, UINT16_MAX, 8340, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8341, UINT16_MAX, 8341, 2578, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9187, UINT16_MAX, 8342, UINT16_MAX, 8342, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8343, UINT16_MAX, 8343, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8344, UINT16_MAX, 8344, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8345, UINT16_MAX, 8345, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8346, UINT16_MAX, 8346, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9189, UINT16_MAX, 8347, UINT16_MAX, 8347, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9191, UINT16_MAX, 8348, UINT16_MAX, 8348, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9193, UINT16_MAX, 8349, UINT16_MAX, 8349, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8350, UINT16_MAX, 8350, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1003, UINT16_MAX, 1003, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8351, UINT16_MAX, 8351, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1004, UINT16_MAX, 1004, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8352, UINT16_MAX, 8352, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1005, UINT16_MAX, 1005, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8353, UINT16_MAX, 8353, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1006, UINT16_MAX, 1006, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8354, UINT16_MAX, 8354, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1007, UINT16_MAX, 1007, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8355, UINT16_MAX, 8355, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1008, UINT16_MAX, 1008, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8356, UINT16_MAX, 8356, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1009, UINT16_MAX, 1009, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8357, UINT16_MAX, 8357, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1010, UINT16_MAX, 1010, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8358, UINT16_MAX, 8358, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1011, UINT16_MAX, 1011, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8359, UINT16_MAX, 8359, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1012, UINT16_MAX, 1012, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8360, UINT16_MAX, 8360, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1013, UINT16_MAX, 1013, UINT16_MAX, 2595, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8361, UINT16_MAX, 8361, 2598, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9206, 1016, UINT16_MAX, 1016, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9209, UINT16_MAX, 8362, UINT16_MAX, 8362, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1019, UINT16_MAX, 1019, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8363, UINT16_MAX, 8363, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1020, UINT16_MAX, 1020, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8364, UINT16_MAX, 8364, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1021, UINT16_MAX, 1021, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8365, UINT16_MAX, 8365, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1022, UINT16_MAX, 1022, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8366, UINT16_MAX, 8366, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1023, UINT16_MAX, 1023, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8367, UINT16_MAX, 8367, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ME, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1024, UINT16_MAX, 1024, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8368, UINT16_MAX, 8368, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1025, UINT16_MAX, 1025, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8369, UINT16_MAX, 8369, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1026, UINT16_MAX, 1026, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8370, UINT16_MAX, 8370, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1027, UINT16_MAX, 1027, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8371, UINT16_MAX, 8371, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1028, UINT16_MAX, 1028, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8372, UINT16_MAX, 8372, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1029, UINT16_MAX, 1029, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8373, UINT16_MAX, 8373, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1030, UINT16_MAX, 1030, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8374, UINT16_MAX, 8374, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1031, UINT16_MAX, 1031, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8375, UINT16_MAX, 8375, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1032, UINT16_MAX, 1032, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8376, UINT16_MAX, 8376, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1033, UINT16_MAX, 1033, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8377, UINT16_MAX, 8377, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1034, UINT16_MAX, 1034, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8378, UINT16_MAX, 8378, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1035, UINT16_MAX, 1035, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8379, UINT16_MAX, 8379, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1036, UINT16_MAX, 1036, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8380, UINT16_MAX, 8380, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1037, UINT16_MAX, 1037, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8381, UINT16_MAX, 8381, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1038, UINT16_MAX, 1038, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8382, UINT16_MAX, 8382, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1039, UINT16_MAX, 1039, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8383, UINT16_MAX, 8383, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1040, UINT16_MAX, 1040, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8384, UINT16_MAX, 8384, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1041, UINT16_MAX, 1041, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8385, UINT16_MAX, 8385, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1042, UINT16_MAX, 1042, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8386, UINT16_MAX, 8386, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1043, UINT16_MAX, 1043, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8387, UINT16_MAX, 8387, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1044, UINT16_MAX, 1044, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8388, UINT16_MAX, 8388, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1045, UINT16_MAX, 1045, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8389, UINT16_MAX, 8389, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1046, UINT16_MAX, 1046, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8390, UINT16_MAX, 8390, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1047, UINT16_MAX, 1047, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8391, UINT16_MAX, 8391, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1048, UINT16_MAX, 1048, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8392, UINT16_MAX, 8392, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1049, UINT16_MAX, 1049, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8393, UINT16_MAX, 8393, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1050, UINT16_MAX, 1050, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8394, UINT16_MAX, 8394, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1051, UINT16_MAX, 1051, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9244, 1054, UINT16_MAX, 1054, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9247, UINT16_MAX, 8395, UINT16_MAX, 8395, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1057, UINT16_MAX, 1057, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8396, UINT16_MAX, 8396, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1058, UINT16_MAX, 1058, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8397, UINT16_MAX, 8397, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1059, UINT16_MAX, 1059, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8398, UINT16_MAX, 8398, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1060, UINT16_MAX, 1060, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8399, UINT16_MAX, 8399, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1061, UINT16_MAX, 1061, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8400, UINT16_MAX, 8400, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1062, UINT16_MAX, 1062, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8401, UINT16_MAX, 8401, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8402, UINT16_MAX, 8402, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9255, 1065, UINT16_MAX, 1065, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9258, UINT16_MAX, 8403, UINT16_MAX, 8403, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9260, 1070, UINT16_MAX, 1070, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9263, UINT16_MAX, 8404, UINT16_MAX, 8404, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1073, UINT16_MAX, 1073, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8405, UINT16_MAX, 8405, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9266, 1076, UINT16_MAX, 1076, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9269, UINT16_MAX, 8406, UINT16_MAX, 8406, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1079, UINT16_MAX, 1079, UINT16_MAX, 2629, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8407, UINT16_MAX, 8407, 2632, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9272, 1082, UINT16_MAX, 1082, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9275, UINT16_MAX, 8408, UINT16_MAX, 8408, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9277, 1087, UINT16_MAX, 1087, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9280, UINT16_MAX, 8409, UINT16_MAX, 8409, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9282, 1092, UINT16_MAX, 1092, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9285, UINT16_MAX, 8410, UINT16_MAX, 8410, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1095, UINT16_MAX, 1095, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8411, UINT16_MAX, 8411, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9288, 1098, UINT16_MAX, 1098, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9291, UINT16_MAX, 8412, UINT16_MAX, 8412, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9293, 1103, UINT16_MAX, 1103, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9296, UINT16_MAX, 8413, UINT16_MAX, 8413, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9298, 1108, UINT16_MAX, 1108, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9301, UINT16_MAX, 8414, UINT16_MAX, 8414, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1111, UINT16_MAX, 1111, UINT16_MAX, 2647, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8415, UINT16_MAX, 8415, 2650, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9304, 1114, UINT16_MAX, 1114, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9307, UINT16_MAX, 8416, UINT16_MAX, 8416, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9309, 1119, UINT16_MAX, 1119, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9312, UINT16_MAX, 8417, UINT16_MAX, 8417, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9314, 1124, UINT16_MAX, 1124, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9317, UINT16_MAX, 8418, UINT16_MAX, 8418, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9319, 1129, UINT16_MAX, 1129, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9322, UINT16_MAX, 8419, UINT16_MAX, 8419, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9324, 1134, UINT16_MAX, 1134, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9327, UINT16_MAX, 8420, UINT16_MAX, 8420, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9329, 1139, UINT16_MAX, 1139, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9332, UINT16_MAX, 8421, UINT16_MAX, 8421, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1142, UINT16_MAX, 1142, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8422, UINT16_MAX, 8422, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9335, 1145, UINT16_MAX, 1145, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9338, UINT16_MAX, 8423, UINT16_MAX, 8423, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1148, UINT16_MAX, 1148, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8424, UINT16_MAX, 8424, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1149, UINT16_MAX, 1149, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8425, UINT16_MAX, 8425, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1150, UINT16_MAX, 1150, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8426, UINT16_MAX, 8426, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1151, UINT16_MAX, 1151, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8427, UINT16_MAX, 8427, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1152, UINT16_MAX, 1152, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8428, UINT16_MAX, 8428, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1153, UINT16_MAX, 1153, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8429, UINT16_MAX, 8429, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1154, UINT16_MAX, 1154, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8430, UINT16_MAX, 8430, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1155, UINT16_MAX, 1155, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8431, UINT16_MAX, 8431, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1156, UINT16_MAX, 1156, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8432, UINT16_MAX, 8432, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1157, UINT16_MAX, 1157, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8433, UINT16_MAX, 8433, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1158, UINT16_MAX, 1158, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8434, UINT16_MAX, 8434, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1159, UINT16_MAX, 1159, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8435, UINT16_MAX, 8435, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1160, UINT16_MAX, 1160, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8436, UINT16_MAX, 8436, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1161, UINT16_MAX, 1161, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8437, UINT16_MAX, 8437, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1162, UINT16_MAX, 1162, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8438, UINT16_MAX, 8438, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1163, UINT16_MAX, 1163, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8439, UINT16_MAX, 8439, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1164, UINT16_MAX, 1164, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8440, UINT16_MAX, 8440, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1165, UINT16_MAX, 1165, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8441, UINT16_MAX, 8441, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1166, UINT16_MAX, 1166, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8442, UINT16_MAX, 8442, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1167, UINT16_MAX, 1167, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8443, UINT16_MAX, 8443, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1168, UINT16_MAX, 1168, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8444, UINT16_MAX, 8444, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1169, UINT16_MAX, 1169, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8445, UINT16_MAX, 8445, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1170, UINT16_MAX, 1170, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8446, UINT16_MAX, 8446, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1171, UINT16_MAX, 1171, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8447, UINT16_MAX, 8447, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1172, UINT16_MAX, 1172, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8448, UINT16_MAX, 8448, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1173, UINT16_MAX, 1173, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8449, UINT16_MAX, 8449, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1174, UINT16_MAX, 1174, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8450, UINT16_MAX, 8450, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1175, UINT16_MAX, 1175, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1176, UINT16_MAX, 1176, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1177, UINT16_MAX, 1177, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1178, UINT16_MAX, 1178, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1179, UINT16_MAX, 1179, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1180, UINT16_MAX, 1180, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1181, UINT16_MAX, 1181, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1182, UINT16_MAX, 1182, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1183, UINT16_MAX, 1183, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1184, UINT16_MAX, 1184, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1185, UINT16_MAX, 1185, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1186, UINT16_MAX, 1186, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1187, UINT16_MAX, 1187, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1188, UINT16_MAX, 1188, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1189, UINT16_MAX, 1189, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1190, UINT16_MAX, 1190, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1191, UINT16_MAX, 1191, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1192, UINT16_MAX, 1192, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1193, UINT16_MAX, 1193, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1194, UINT16_MAX, 1194, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1195, UINT16_MAX, 1195, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1196, UINT16_MAX, 1196, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1197, UINT16_MAX, 1197, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1198, UINT16_MAX, 1198, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1199, UINT16_MAX, 1199, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1200, UINT16_MAX, 1200, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1201, UINT16_MAX, 1201, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1202, UINT16_MAX, 1202, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1203, UINT16_MAX, 1203, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1204, UINT16_MAX, 1204, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1205, UINT16_MAX, 1205, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1206, UINT16_MAX, 1206, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1207, UINT16_MAX, 1207, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1208, UINT16_MAX, 1208, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1209, UINT16_MAX, 1209, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1210, UINT16_MAX, 1210, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1211, UINT16_MAX, 1211, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1212, UINT16_MAX, 1212, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8451, UINT16_MAX, 8451, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8452, UINT16_MAX, 8452, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8453, UINT16_MAX, 8453, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8454, UINT16_MAX, 8454, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8455, UINT16_MAX, 8455, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8456, UINT16_MAX, 8456, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8457, UINT16_MAX, 8457, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8458, UINT16_MAX, 8458, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8459, UINT16_MAX, 8459, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8460, UINT16_MAX, 8460, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8461, UINT16_MAX, 8461, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8462, UINT16_MAX, 8462, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8463, UINT16_MAX, 8463, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8464, UINT16_MAX, 8464, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8465, UINT16_MAX, 8465, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8466, UINT16_MAX, 8466, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8467, UINT16_MAX, 8467, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8468, UINT16_MAX, 8468, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8469, UINT16_MAX, 8469, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8470, UINT16_MAX, 8470, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8471, UINT16_MAX, 8471, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8472, UINT16_MAX, 8472, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8473, UINT16_MAX, 8473, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8474, UINT16_MAX, 8474, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8475, UINT16_MAX, 8475, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8476, UINT16_MAX, 8476, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8477, UINT16_MAX, 8477, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8478, UINT16_MAX, 8478, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8479, UINT16_MAX, 8479, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8480, UINT16_MAX, 8480, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8481, UINT16_MAX, 8481, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8482, UINT16_MAX, 8482, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8483, UINT16_MAX, 8483, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8484, UINT16_MAX, 8484, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8485, UINT16_MAX, 8485, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8486, UINT16_MAX, 8486, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8487, UINT16_MAX, 8487, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8488, UINT16_MAX, 8488, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9405, 9405, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 222, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 228, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 10, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 11, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 12, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 13, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 14, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 15, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 16, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 17, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 18, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 19, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 20, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 21, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 22, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 23, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 24, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 25, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_AN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_PREPEND}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 30, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 31, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 32, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9407, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9409, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9411, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9413, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9415, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2671, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2676, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2679, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 27, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 28, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 29, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 33, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 34, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32785, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 230, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32786, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 220, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32787, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_AN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_AN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 35, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, 9417, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, 9419, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, 9421, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_COMPAT, 9423, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9425, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2685, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9427, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2688, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, 9429, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2682, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_PREPEND}, + {UTF8PROC_CATEGORY_MN, 36, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2691, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9431, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2694, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9433, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2697, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9435, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 7, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32788, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 9, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9437, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9439, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9441, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9445, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9447, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9451, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 7, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32789, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2700, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9453, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9455, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32790, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9457, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9459, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9461, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9463, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9465, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9467, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9469, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9471, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9473, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32792, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2704, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9475, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9477, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9479, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32791, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32793, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9481, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9483, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2709, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9485, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32795, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2712, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2716, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9487, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9489, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32794, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2719, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 84, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 91, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32796, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2722, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32799, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2725, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2730, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32797, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32798, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32800, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2733, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2737, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_PREPEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32801, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 9, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32802, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32803, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2740, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9513, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2745, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9515, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32804, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MN, 103, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 107, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9521, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MN, 118, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 122, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 9525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NOBREAK, 1335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 216, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9528, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 129, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 130, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 132, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9542, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9544, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, UTF8PROC_DECOMP_TYPE_COMPAT, 9546, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9548, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, UTF8PROC_DECOMP_TYPE_COMPAT, 9550, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9552, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9554, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9556, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9560, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9562, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 9564, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2748, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9566, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32805, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1376, UINT16_MAX, 1376, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1377, UINT16_MAX, 1377, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1378, UINT16_MAX, 1378, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1379, UINT16_MAX, 1379, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1380, UINT16_MAX, 1380, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1381, UINT16_MAX, 1381, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1382, UINT16_MAX, 1382, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1383, UINT16_MAX, 1383, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1384, UINT16_MAX, 1384, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1385, UINT16_MAX, 1385, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1386, UINT16_MAX, 1386, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1387, UINT16_MAX, 1387, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1388, UINT16_MAX, 1388, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1389, UINT16_MAX, 1389, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1390, UINT16_MAX, 1390, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1391, UINT16_MAX, 1391, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1392, UINT16_MAX, 1392, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1393, UINT16_MAX, 1393, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1394, UINT16_MAX, 1394, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1395, UINT16_MAX, 1395, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1396, UINT16_MAX, 1396, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1397, UINT16_MAX, 1397, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1398, UINT16_MAX, 1398, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1399, UINT16_MAX, 1399, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1400, UINT16_MAX, 1400, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1401, UINT16_MAX, 1401, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1402, UINT16_MAX, 1402, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1403, UINT16_MAX, 1403, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1404, UINT16_MAX, 1404, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1405, UINT16_MAX, 1405, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1406, UINT16_MAX, 1406, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1407, UINT16_MAX, 1407, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1408, UINT16_MAX, 1408, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1409, UINT16_MAX, 1409, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1410, UINT16_MAX, 1410, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1411, UINT16_MAX, 1411, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1412, UINT16_MAX, 1412, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1413, UINT16_MAX, 1413, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1414, UINT16_MAX, 1414, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1415, UINT16_MAX, 1415, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8489, UINT16_MAX, 1446, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8490, UINT16_MAX, 1447, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8491, UINT16_MAX, 1448, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8492, UINT16_MAX, 1449, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8493, UINT16_MAX, 1450, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8494, UINT16_MAX, 1451, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8495, UINT16_MAX, 1452, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8496, UINT16_MAX, 1453, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8497, UINT16_MAX, 1454, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8498, UINT16_MAX, 1455, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8499, UINT16_MAX, 1456, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8500, UINT16_MAX, 1457, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8501, UINT16_MAX, 1416, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8502, UINT16_MAX, 1458, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8503, UINT16_MAX, 1459, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8504, UINT16_MAX, 1460, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8505, UINT16_MAX, 1461, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8506, UINT16_MAX, 1462, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8507, UINT16_MAX, 1463, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8508, UINT16_MAX, 1464, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8509, UINT16_MAX, 1465, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8510, UINT16_MAX, 1466, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8511, UINT16_MAX, 1467, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8512, UINT16_MAX, 1468, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8513, UINT16_MAX, 1469, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8514, UINT16_MAX, 1470, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8515, UINT16_MAX, 1471, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8516, UINT16_MAX, 1472, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8517, UINT16_MAX, 1473, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8518, UINT16_MAX, 1474, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8519, UINT16_MAX, 1475, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8520, UINT16_MAX, 1476, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8521, UINT16_MAX, 1477, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8522, UINT16_MAX, 1478, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8523, UINT16_MAX, 1479, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8524, UINT16_MAX, 1480, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8525, UINT16_MAX, 1481, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8526, UINT16_MAX, 1482, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8527, UINT16_MAX, 1483, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8528, UINT16_MAX, 1484, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8529, UINT16_MAX, 1485, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8530, UINT16_MAX, 1486, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8531, UINT16_MAX, 1487, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1416, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8532, UINT16_MAX, 1488, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8533, UINT16_MAX, 1489, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8534, UINT16_MAX, 1490, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_L}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, false, 2, 0, UTF8PROC_BOUNDCLASS_L}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, false, 1, 0, UTF8PROC_BOUNDCLASS_V}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_V}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_T}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8535, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8536, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8537, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8538, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8539, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8540, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8541, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8542, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8543, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8544, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8545, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8546, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8547, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8548, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8549, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8550, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8551, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8552, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8553, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8554, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8555, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8556, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8557, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8558, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8559, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8560, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8561, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8562, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8563, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8564, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8565, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8566, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8567, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8568, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8569, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8570, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8571, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8572, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8573, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8574, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8575, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8576, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8577, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8578, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8579, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8580, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8581, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8582, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8583, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8584, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8585, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8586, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8587, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8588, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8589, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8590, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8591, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8592, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8593, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8594, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8595, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8596, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8597, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8598, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8599, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8600, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8601, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8602, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8603, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8604, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8605, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8606, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8607, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8608, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8609, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8610, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8611, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8612, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8613, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8614, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8615, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8616, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8617, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8618, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8619, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, 8620, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1417, 1417, UINT16_MAX, 1417, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1418, 1418, UINT16_MAX, 1418, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1419, 1419, UINT16_MAX, 1419, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1420, 1420, UINT16_MAX, 1420, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1421, 1421, UINT16_MAX, 1421, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1422, 1422, UINT16_MAX, 1422, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2751, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9615, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2754, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9617, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2757, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9619, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2760, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9621, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2763, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9623, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2766, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 9625, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32806, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2769, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9627, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2772, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9629, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2775, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2778, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9631, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9633, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 2781, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 9635, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 9, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 955, 8305, UINT16_MAX, 8305, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 957, 8307, UINT16_MAX, 8307, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 969, 8317, UINT16_MAX, 8317, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 972, 8320, UINT16_MAX, 8320, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 973, 8321, UINT16_MAX, 8321, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 981, 8329, UINT16_MAX, 8329, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1004, 8352, UINT16_MAX, 8352, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1445, 8621, UINT16_MAX, 8621, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1446, UINT16_MAX, 1446, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1447, UINT16_MAX, 1447, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1448, UINT16_MAX, 1448, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1449, UINT16_MAX, 1449, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1450, UINT16_MAX, 1450, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1451, UINT16_MAX, 1451, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1452, UINT16_MAX, 1452, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1453, UINT16_MAX, 1453, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1454, UINT16_MAX, 1454, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1455, UINT16_MAX, 1455, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1456, UINT16_MAX, 1456, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1457, UINT16_MAX, 1457, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1416, UINT16_MAX, 1416, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1458, UINT16_MAX, 1458, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1459, UINT16_MAX, 1459, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1460, UINT16_MAX, 1460, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1461, UINT16_MAX, 1461, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1462, UINT16_MAX, 1462, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1463, UINT16_MAX, 1463, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1464, UINT16_MAX, 1464, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1465, UINT16_MAX, 1465, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1466, UINT16_MAX, 1466, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1467, UINT16_MAX, 1467, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1468, UINT16_MAX, 1468, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1469, UINT16_MAX, 1469, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1470, UINT16_MAX, 1470, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1471, UINT16_MAX, 1471, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1472, UINT16_MAX, 1472, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1473, UINT16_MAX, 1473, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1474, UINT16_MAX, 1474, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1475, UINT16_MAX, 1475, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1476, UINT16_MAX, 1476, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1477, UINT16_MAX, 1477, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1478, UINT16_MAX, 1478, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1479, UINT16_MAX, 1479, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1480, UINT16_MAX, 1480, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1481, UINT16_MAX, 1481, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1482, UINT16_MAX, 1482, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1483, UINT16_MAX, 1483, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1484, UINT16_MAX, 1484, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1485, UINT16_MAX, 1485, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1486, UINT16_MAX, 1486, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1487, UINT16_MAX, 1487, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1488, UINT16_MAX, 1488, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1489, UINT16_MAX, 1489, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1490, UINT16_MAX, 1490, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1492, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1494, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1496, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1500, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1512, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1513, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 4, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 485, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 486, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1515, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 6, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 10, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 12, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 355, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 14, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 479, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 15, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 19, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 20, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 21, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 846, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 847, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 848, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 863, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 864, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 8, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 17, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 20, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 21, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 846, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 847, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 859, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 863, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 864, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 968, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8622, UINT16_MAX, 8622, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8623, UINT16_MAX, 8623, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8624, UINT16_MAX, 8624, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 2, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1521, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 94, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 5, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1522, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 490, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1527, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1528, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1529, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1531, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 494, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1533, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1535, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 778, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 513, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 779, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 25, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1539, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 1540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 852, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 214, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9733, 1543, UINT16_MAX, 1543, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9736, UINT16_MAX, 8625, UINT16_MAX, 8625, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9738, 1548, UINT16_MAX, 1548, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9741, UINT16_MAX, 8626, UINT16_MAX, 8626, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9743, 1553, UINT16_MAX, 1553, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9746, UINT16_MAX, 8627, UINT16_MAX, 8627, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9748, 1558, UINT16_MAX, 1558, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9751, UINT16_MAX, 8628, UINT16_MAX, 8628, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9753, 1563, UINT16_MAX, 1563, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9756, UINT16_MAX, 8629, UINT16_MAX, 8629, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9758, 1568, UINT16_MAX, 1568, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9761, UINT16_MAX, 8630, UINT16_MAX, 8630, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9763, 1573, UINT16_MAX, 1573, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9766, UINT16_MAX, 8631, UINT16_MAX, 8631, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9768, 1578, UINT16_MAX, 1578, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9771, UINT16_MAX, 8632, UINT16_MAX, 8632, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9773, 1583, UINT16_MAX, 1583, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9776, UINT16_MAX, 8633, UINT16_MAX, 8633, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9778, 1588, UINT16_MAX, 1588, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9781, UINT16_MAX, 8634, UINT16_MAX, 8634, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9783, 1593, UINT16_MAX, 1593, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9786, UINT16_MAX, 8635, UINT16_MAX, 8635, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9788, 1598, UINT16_MAX, 1598, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9791, UINT16_MAX, 8636, UINT16_MAX, 8636, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9793, 1603, UINT16_MAX, 1603, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9796, UINT16_MAX, 8637, UINT16_MAX, 8637, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9798, 1608, UINT16_MAX, 1608, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9801, UINT16_MAX, 8638, UINT16_MAX, 8638, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9803, 1613, UINT16_MAX, 1613, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9806, UINT16_MAX, 8639, UINT16_MAX, 8639, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9808, 1618, UINT16_MAX, 1618, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9811, UINT16_MAX, 8640, UINT16_MAX, 8640, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9813, 1623, UINT16_MAX, 1623, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9816, UINT16_MAX, 8641, UINT16_MAX, 8641, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9818, 1628, UINT16_MAX, 1628, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9821, UINT16_MAX, 8642, UINT16_MAX, 8642, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9823, 1633, UINT16_MAX, 1633, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9826, UINT16_MAX, 8643, UINT16_MAX, 8643, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9828, 1638, UINT16_MAX, 1638, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9831, UINT16_MAX, 8644, UINT16_MAX, 8644, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9833, 1643, UINT16_MAX, 1643, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9836, UINT16_MAX, 8645, UINT16_MAX, 8645, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9838, 1648, UINT16_MAX, 1648, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9841, UINT16_MAX, 8646, UINT16_MAX, 8646, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9843, 1653, UINT16_MAX, 1653, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9846, UINT16_MAX, 8647, UINT16_MAX, 8647, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9848, 1658, UINT16_MAX, 1658, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9851, UINT16_MAX, 8648, UINT16_MAX, 8648, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9853, 1663, UINT16_MAX, 1663, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9856, UINT16_MAX, 8649, UINT16_MAX, 8649, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9858, 1668, UINT16_MAX, 1668, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9861, UINT16_MAX, 8650, UINT16_MAX, 8650, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9863, 1673, UINT16_MAX, 1673, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9866, UINT16_MAX, 8651, UINT16_MAX, 8651, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9868, 1678, UINT16_MAX, 1678, UINT16_MAX, 2884, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9871, UINT16_MAX, 8652, UINT16_MAX, 8652, 2887, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9873, 1683, UINT16_MAX, 1683, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9876, UINT16_MAX, 8653, UINT16_MAX, 8653, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9878, 1688, UINT16_MAX, 1688, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9881, UINT16_MAX, 8654, UINT16_MAX, 8654, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9883, 1693, UINT16_MAX, 1693, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9886, UINT16_MAX, 8655, UINT16_MAX, 8655, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9888, 1698, UINT16_MAX, 1698, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9891, UINT16_MAX, 8656, UINT16_MAX, 8656, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9893, 1703, UINT16_MAX, 1703, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9896, UINT16_MAX, 8657, UINT16_MAX, 8657, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9898, 1708, UINT16_MAX, 1708, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9901, UINT16_MAX, 8658, UINT16_MAX, 8658, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9903, 1713, UINT16_MAX, 1713, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9906, UINT16_MAX, 8659, UINT16_MAX, 8659, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9908, 1718, UINT16_MAX, 1718, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9911, UINT16_MAX, 8660, UINT16_MAX, 8660, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9913, 1723, UINT16_MAX, 1723, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9916, UINT16_MAX, 8661, UINT16_MAX, 8661, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9918, 1728, UINT16_MAX, 1728, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9921, UINT16_MAX, 8662, UINT16_MAX, 8662, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9923, 1733, UINT16_MAX, 1733, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9926, UINT16_MAX, 8663, UINT16_MAX, 8663, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9928, 1738, UINT16_MAX, 1738, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9931, UINT16_MAX, 8664, UINT16_MAX, 8664, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9933, 1743, UINT16_MAX, 1743, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9936, UINT16_MAX, 8665, UINT16_MAX, 8665, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9938, 1748, UINT16_MAX, 1748, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9941, UINT16_MAX, 8666, UINT16_MAX, 8666, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9943, 1753, UINT16_MAX, 1753, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9946, UINT16_MAX, 8667, UINT16_MAX, 8667, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9948, 1758, UINT16_MAX, 1758, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9951, UINT16_MAX, 8668, UINT16_MAX, 8668, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9953, 1763, UINT16_MAX, 1763, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9956, UINT16_MAX, 8669, UINT16_MAX, 8669, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9958, 1768, UINT16_MAX, 1768, UINT16_MAX, 3006, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9961, UINT16_MAX, 8670, UINT16_MAX, 8670, 3009, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9963, 1773, UINT16_MAX, 1773, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9966, UINT16_MAX, 8671, UINT16_MAX, 8671, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9968, 1778, UINT16_MAX, 1778, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9971, UINT16_MAX, 8672, UINT16_MAX, 8672, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9973, 1783, UINT16_MAX, 1783, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9976, UINT16_MAX, 8673, UINT16_MAX, 8673, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9978, 1788, UINT16_MAX, 1788, UINT16_MAX, 3024, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9981, UINT16_MAX, 8674, UINT16_MAX, 8674, 3027, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9983, 1793, UINT16_MAX, 1793, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9986, UINT16_MAX, 8675, UINT16_MAX, 8675, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9988, 1798, UINT16_MAX, 1798, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9991, UINT16_MAX, 8676, UINT16_MAX, 8676, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9993, 1803, UINT16_MAX, 1803, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 9996, UINT16_MAX, 8677, UINT16_MAX, 8677, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 9998, 1808, UINT16_MAX, 1808, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10001, UINT16_MAX, 8678, UINT16_MAX, 8678, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10003, 1813, UINT16_MAX, 1813, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10006, UINT16_MAX, 8679, UINT16_MAX, 8679, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10008, 1818, UINT16_MAX, 1818, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10011, UINT16_MAX, 8680, UINT16_MAX, 8680, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10013, 1823, UINT16_MAX, 1823, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10016, UINT16_MAX, 8681, UINT16_MAX, 8681, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10018, 1828, UINT16_MAX, 1828, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10021, UINT16_MAX, 8682, UINT16_MAX, 8682, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10023, 1833, UINT16_MAX, 1833, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10026, UINT16_MAX, 8683, UINT16_MAX, 8683, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10028, 1838, UINT16_MAX, 1838, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10031, UINT16_MAX, 8684, UINT16_MAX, 8684, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10033, 1843, UINT16_MAX, 1843, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10036, UINT16_MAX, 8685, UINT16_MAX, 8685, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10038, 1848, UINT16_MAX, 1848, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10041, UINT16_MAX, 8686, UINT16_MAX, 8686, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10043, 1853, UINT16_MAX, 1853, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10046, UINT16_MAX, 8687, UINT16_MAX, 8687, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10048, 1858, UINT16_MAX, 1858, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10051, UINT16_MAX, 8688, UINT16_MAX, 8688, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10053, 1863, UINT16_MAX, 1863, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10056, UINT16_MAX, 8689, UINT16_MAX, 8689, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10058, 1868, UINT16_MAX, 1868, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10061, UINT16_MAX, 8690, UINT16_MAX, 8690, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10063, 1873, UINT16_MAX, 1873, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10066, UINT16_MAX, 8691, UINT16_MAX, 8691, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10068, 1878, UINT16_MAX, 1878, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10071, UINT16_MAX, 8692, UINT16_MAX, 8692, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10073, 1883, UINT16_MAX, 1883, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10076, UINT16_MAX, 8693, UINT16_MAX, 8693, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10078, 1888, UINT16_MAX, 1888, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10081, UINT16_MAX, 8694, UINT16_MAX, 8694, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10083, 1893, UINT16_MAX, 1893, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10086, UINT16_MAX, 8695, UINT16_MAX, 8695, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10088, 1898, UINT16_MAX, 1898, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10091, UINT16_MAX, 8696, UINT16_MAX, 8696, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10093, 1903, UINT16_MAX, 1903, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10096, UINT16_MAX, 8697, UINT16_MAX, 8697, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10098, 1908, UINT16_MAX, 1908, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10101, UINT16_MAX, 8698, UINT16_MAX, 8698, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10103, 1913, UINT16_MAX, 1913, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10106, UINT16_MAX, 8699, UINT16_MAX, 8699, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10108, 10108, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10110, 10110, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10112, 10112, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10114, 10114, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 10116, 10116, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10118, 1783, 8673, UINT16_MAX, 8673, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 8322, UINT16_MAX, 8700, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10120, 1930, UINT16_MAX, 1930, UINT16_MAX, 3241, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10123, UINT16_MAX, 8701, UINT16_MAX, 8701, 3250, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10125, 1935, UINT16_MAX, 1935, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10128, UINT16_MAX, 8702, UINT16_MAX, 8702, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10130, 1940, UINT16_MAX, 1940, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10133, UINT16_MAX, 8703, UINT16_MAX, 8703, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10135, 1945, UINT16_MAX, 1945, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10138, UINT16_MAX, 8704, UINT16_MAX, 8704, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10140, 1950, UINT16_MAX, 1950, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10143, UINT16_MAX, 8705, UINT16_MAX, 8705, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10145, 1955, UINT16_MAX, 1955, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10148, UINT16_MAX, 8706, UINT16_MAX, 8706, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10150, 1960, UINT16_MAX, 1960, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10153, UINT16_MAX, 8707, UINT16_MAX, 8707, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10155, 1965, UINT16_MAX, 1965, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10158, UINT16_MAX, 8708, UINT16_MAX, 8708, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10160, 1970, UINT16_MAX, 1970, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10163, UINT16_MAX, 8709, UINT16_MAX, 8709, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10165, 1975, UINT16_MAX, 1975, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10168, UINT16_MAX, 8710, UINT16_MAX, 8710, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10170, 1980, UINT16_MAX, 1980, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10173, UINT16_MAX, 8711, UINT16_MAX, 8711, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10175, 1985, UINT16_MAX, 1985, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10178, UINT16_MAX, 8712, UINT16_MAX, 8712, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10180, 1990, UINT16_MAX, 1990, UINT16_MAX, 3455, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10183, UINT16_MAX, 8713, UINT16_MAX, 8713, 3458, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10185, 1995, UINT16_MAX, 1995, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10188, UINT16_MAX, 8714, UINT16_MAX, 8714, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10190, 2000, UINT16_MAX, 2000, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10193, UINT16_MAX, 8715, UINT16_MAX, 8715, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10195, 2005, UINT16_MAX, 2005, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10198, UINT16_MAX, 8716, UINT16_MAX, 8716, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10200, 2010, UINT16_MAX, 2010, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10203, UINT16_MAX, 8717, UINT16_MAX, 8717, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10205, 2015, UINT16_MAX, 2015, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10208, UINT16_MAX, 8718, UINT16_MAX, 8718, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10210, 2020, UINT16_MAX, 2020, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10213, UINT16_MAX, 8719, UINT16_MAX, 8719, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10215, 2025, UINT16_MAX, 2025, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10218, UINT16_MAX, 8720, UINT16_MAX, 8720, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10220, 2030, UINT16_MAX, 2030, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10223, UINT16_MAX, 8721, UINT16_MAX, 8721, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10225, 2035, UINT16_MAX, 2035, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10228, UINT16_MAX, 8722, UINT16_MAX, 8722, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10230, 2040, UINT16_MAX, 2040, UINT16_MAX, 3559, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10233, UINT16_MAX, 8723, UINT16_MAX, 8723, 3562, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10235, 2045, UINT16_MAX, 2045, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10238, UINT16_MAX, 8724, UINT16_MAX, 8724, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10240, 2050, UINT16_MAX, 2050, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10243, UINT16_MAX, 8725, UINT16_MAX, 8725, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10245, 2055, UINT16_MAX, 2055, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10248, UINT16_MAX, 8726, UINT16_MAX, 8726, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10250, 2060, UINT16_MAX, 2060, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10253, UINT16_MAX, 8727, UINT16_MAX, 8727, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10255, 2065, UINT16_MAX, 2065, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10258, UINT16_MAX, 8728, UINT16_MAX, 8728, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10260, 2070, UINT16_MAX, 2070, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10263, UINT16_MAX, 8729, UINT16_MAX, 8729, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10265, 2075, UINT16_MAX, 2075, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10268, UINT16_MAX, 8730, UINT16_MAX, 8730, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10270, 2080, UINT16_MAX, 2080, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10273, UINT16_MAX, 8731, UINT16_MAX, 8731, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10275, 2085, UINT16_MAX, 2085, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10278, UINT16_MAX, 8732, UINT16_MAX, 8732, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10280, 2090, UINT16_MAX, 2090, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10283, UINT16_MAX, 8733, UINT16_MAX, 8733, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10285, 2095, UINT16_MAX, 2095, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10288, UINT16_MAX, 8734, UINT16_MAX, 8734, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10290, 2100, UINT16_MAX, 2100, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10293, UINT16_MAX, 8735, UINT16_MAX, 8735, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10295, 2105, UINT16_MAX, 2105, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10298, UINT16_MAX, 8736, UINT16_MAX, 8736, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10300, 2110, UINT16_MAX, 2110, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10303, UINT16_MAX, 8737, UINT16_MAX, 8737, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10305, 2115, UINT16_MAX, 2115, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10308, UINT16_MAX, 8738, UINT16_MAX, 8738, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10310, 2120, UINT16_MAX, 2120, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10313, UINT16_MAX, 8739, UINT16_MAX, 8739, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10315, 2125, UINT16_MAX, 2125, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10318, UINT16_MAX, 8740, UINT16_MAX, 8740, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10320, 2130, UINT16_MAX, 2130, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10323, UINT16_MAX, 8741, UINT16_MAX, 8741, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10325, 2135, UINT16_MAX, 2135, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10328, UINT16_MAX, 8742, UINT16_MAX, 8742, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10330, 2140, UINT16_MAX, 2140, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10333, UINT16_MAX, 8743, UINT16_MAX, 8743, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10335, 2145, UINT16_MAX, 2145, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10338, UINT16_MAX, 8744, UINT16_MAX, 8744, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10340, 2150, UINT16_MAX, 2150, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10343, UINT16_MAX, 8745, UINT16_MAX, 8745, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 2153, UINT16_MAX, 2153, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8746, UINT16_MAX, 8746, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 2154, UINT16_MAX, 2154, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8747, UINT16_MAX, 8747, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 2155, UINT16_MAX, 2155, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8748, UINT16_MAX, 8748, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10348, UINT16_MAX, 8749, UINT16_MAX, 8749, 3761, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10350, UINT16_MAX, 8750, UINT16_MAX, 8750, 3814, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10352, UINT16_MAX, 8751, UINT16_MAX, 8751, 4793, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10354, UINT16_MAX, 8752, UINT16_MAX, 8752, 4796, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10356, UINT16_MAX, 8753, UINT16_MAX, 8753, 4799, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10358, UINT16_MAX, 8754, UINT16_MAX, 8754, 4802, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10360, UINT16_MAX, 8755, UINT16_MAX, 8755, 4805, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10362, UINT16_MAX, 8756, UINT16_MAX, 8756, 4808, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10364, 2174, UINT16_MAX, 2174, UINT16_MAX, 3867, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10367, 2177, UINT16_MAX, 2177, UINT16_MAX, 3920, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10370, 2180, UINT16_MAX, 2180, UINT16_MAX, 4811, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10373, 2183, UINT16_MAX, 2183, UINT16_MAX, 4814, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10376, 2186, UINT16_MAX, 2186, UINT16_MAX, 4817, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10379, 2189, UINT16_MAX, 2189, UINT16_MAX, 4820, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10382, 2192, UINT16_MAX, 2192, UINT16_MAX, 4823, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10385, 2195, UINT16_MAX, 2195, UINT16_MAX, 4826, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10388, UINT16_MAX, 8757, UINT16_MAX, 8757, 3973, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10390, UINT16_MAX, 8758, UINT16_MAX, 8758, 3977, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10392, UINT16_MAX, 8759, UINT16_MAX, 8759, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10394, UINT16_MAX, 8760, UINT16_MAX, 8760, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10396, UINT16_MAX, 8761, UINT16_MAX, 8761, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10398, UINT16_MAX, 8762, UINT16_MAX, 8762, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10400, 2210, UINT16_MAX, 2210, UINT16_MAX, 3981, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10403, 2213, UINT16_MAX, 2213, UINT16_MAX, 3985, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10406, 2216, UINT16_MAX, 2216, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10409, 2219, UINT16_MAX, 2219, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10412, 2222, UINT16_MAX, 2222, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10415, 2225, UINT16_MAX, 2225, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10418, UINT16_MAX, 8763, UINT16_MAX, 8763, 3989, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10420, UINT16_MAX, 8764, UINT16_MAX, 8764, 4042, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10422, UINT16_MAX, 8765, UINT16_MAX, 8765, 4829, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10424, UINT16_MAX, 8766, UINT16_MAX, 8766, 4832, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10426, UINT16_MAX, 8767, UINT16_MAX, 8767, 4835, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10428, UINT16_MAX, 8768, UINT16_MAX, 8768, 4838, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10430, UINT16_MAX, 8769, UINT16_MAX, 8769, 4841, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10432, UINT16_MAX, 8770, UINT16_MAX, 8770, 4844, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10434, 2244, UINT16_MAX, 2244, UINT16_MAX, 4095, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10437, 2247, UINT16_MAX, 2247, UINT16_MAX, 4148, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10440, 2250, UINT16_MAX, 2250, UINT16_MAX, 4847, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10443, 2253, UINT16_MAX, 2253, UINT16_MAX, 4850, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10446, 2256, UINT16_MAX, 2256, UINT16_MAX, 4853, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10449, 2259, UINT16_MAX, 2259, UINT16_MAX, 4856, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10452, 2262, UINT16_MAX, 2262, UINT16_MAX, 4859, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10455, 2265, UINT16_MAX, 2265, UINT16_MAX, 4862, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10458, UINT16_MAX, 8771, UINT16_MAX, 8771, 4201, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10460, UINT16_MAX, 8772, UINT16_MAX, 8772, 4253, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10462, UINT16_MAX, 8773, UINT16_MAX, 8773, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10464, UINT16_MAX, 8774, UINT16_MAX, 8774, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10466, UINT16_MAX, 8775, UINT16_MAX, 8775, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10468, UINT16_MAX, 8776, UINT16_MAX, 8776, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10470, UINT16_MAX, 8777, UINT16_MAX, 8777, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10472, UINT16_MAX, 8778, UINT16_MAX, 8778, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10474, 2284, UINT16_MAX, 2284, UINT16_MAX, 4305, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10477, 2287, UINT16_MAX, 2287, UINT16_MAX, 4357, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10480, 2290, UINT16_MAX, 2290, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10483, 2293, UINT16_MAX, 2293, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10486, 2296, UINT16_MAX, 2296, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10489, 2299, UINT16_MAX, 2299, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10492, 2302, UINT16_MAX, 2302, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10495, 2305, UINT16_MAX, 2305, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10498, UINT16_MAX, 8779, UINT16_MAX, 8779, 4409, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10500, UINT16_MAX, 8780, UINT16_MAX, 8780, 4413, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10502, UINT16_MAX, 8781, UINT16_MAX, 8781, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10504, UINT16_MAX, 8782, UINT16_MAX, 8782, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10506, UINT16_MAX, 8783, UINT16_MAX, 8783, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10508, UINT16_MAX, 8784, UINT16_MAX, 8784, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10510, 2320, UINT16_MAX, 2320, UINT16_MAX, 4417, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10513, 2323, UINT16_MAX, 2323, UINT16_MAX, 4421, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10516, 2326, UINT16_MAX, 2326, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10519, 2329, UINT16_MAX, 2329, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10522, 2332, UINT16_MAX, 2332, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10525, 2335, UINT16_MAX, 2335, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10528, 10528, UINT16_MAX, UINT16_MAX, UINT16_MAX, 4425, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10530, UINT16_MAX, 8785, UINT16_MAX, 8785, 4477, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10532, 18726, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10537, UINT16_MAX, 8786, UINT16_MAX, 8786, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10539, 18733, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10544, UINT16_MAX, 8787, UINT16_MAX, 8787, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10546, 18740, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10551, UINT16_MAX, 8788, UINT16_MAX, 8788, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10553, 2363, UINT16_MAX, 2363, UINT16_MAX, 4529, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10556, 2366, UINT16_MAX, 2366, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10559, 2369, UINT16_MAX, 2369, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10562, 2372, UINT16_MAX, 2372, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10565, UINT16_MAX, 8789, UINT16_MAX, 8789, 4581, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10567, UINT16_MAX, 8790, UINT16_MAX, 8790, 4634, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10569, UINT16_MAX, 8791, UINT16_MAX, 8791, 4865, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10571, UINT16_MAX, 8792, UINT16_MAX, 8792, 4868, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10573, UINT16_MAX, 8793, UINT16_MAX, 8793, 4871, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10575, UINT16_MAX, 8794, UINT16_MAX, 8794, 4874, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10577, UINT16_MAX, 8795, UINT16_MAX, 8795, 4877, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10579, UINT16_MAX, 8796, UINT16_MAX, 8796, 4880, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10581, 2391, UINT16_MAX, 2391, UINT16_MAX, 4687, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10584, 2394, UINT16_MAX, 2394, UINT16_MAX, 4740, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10587, 2397, UINT16_MAX, 2397, UINT16_MAX, 4883, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10590, 2400, UINT16_MAX, 2400, UINT16_MAX, 4886, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10593, 2403, UINT16_MAX, 2403, UINT16_MAX, 4889, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10596, 2406, UINT16_MAX, 2406, UINT16_MAX, 4892, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10599, 2409, UINT16_MAX, 2409, UINT16_MAX, 4895, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10602, 2412, UINT16_MAX, 2412, UINT16_MAX, 4898, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10605, UINT16_MAX, 8797, UINT16_MAX, 8797, 4901, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 820, UINT16_MAX, 8798, UINT16_MAX, 8798, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10607, UINT16_MAX, 8799, UINT16_MAX, 8799, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 824, UINT16_MAX, 8800, UINT16_MAX, 8800, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10609, UINT16_MAX, 8801, UINT16_MAX, 8801, 4910, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 827, UINT16_MAX, 8802, UINT16_MAX, 8802, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10611, UINT16_MAX, 8803, UINT16_MAX, 8803, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 830, UINT16_MAX, 8804, UINT16_MAX, 8804, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10613, UINT16_MAX, 8805, UINT16_MAX, 8805, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 833, UINT16_MAX, 8806, UINT16_MAX, 8806, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10615, UINT16_MAX, 8807, UINT16_MAX, 8807, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 836, UINT16_MAX, 8808, UINT16_MAX, 8808, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10617, UINT16_MAX, 8809, UINT16_MAX, 8809, 5030, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 839, UINT16_MAX, 8810, UINT16_MAX, 8810, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10619, 10621, 8811, UINT16_MAX, 8811, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10623, 10625, 8812, UINT16_MAX, 8812, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10627, 10629, 8813, UINT16_MAX, 8813, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10631, 10633, 8814, UINT16_MAX, 8814, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10635, 10637, 8815, UINT16_MAX, 8815, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10639, 10641, 8816, UINT16_MAX, 8816, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10643, 10645, 8817, UINT16_MAX, 8817, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10647, 10649, 8818, UINT16_MAX, 8818, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10651, 10621, UINT16_MAX, 8819, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10653, 10625, UINT16_MAX, 8820, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10655, 10629, UINT16_MAX, 8821, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10657, 10633, UINT16_MAX, 8822, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10659, 10637, UINT16_MAX, 8823, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10661, 10641, UINT16_MAX, 8824, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10663, 10645, UINT16_MAX, 8825, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10665, 10649, UINT16_MAX, 8826, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10667, 10669, 8827, UINT16_MAX, 8827, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10671, 10673, 8828, UINT16_MAX, 8828, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10675, 10677, 8829, UINT16_MAX, 8829, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10679, 10681, 8830, UINT16_MAX, 8830, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10683, 10685, 8831, UINT16_MAX, 8831, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10687, 10689, 8832, UINT16_MAX, 8832, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10691, 10693, 8833, UINT16_MAX, 8833, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10695, 10697, 8834, UINT16_MAX, 8834, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10699, 10669, UINT16_MAX, 8835, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10701, 10673, UINT16_MAX, 8836, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10703, 10677, UINT16_MAX, 8837, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10705, 10681, UINT16_MAX, 8838, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10707, 10685, UINT16_MAX, 8839, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10709, 10689, UINT16_MAX, 8840, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10711, 10693, UINT16_MAX, 8841, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10713, 10697, UINT16_MAX, 8842, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10715, 10717, 8843, UINT16_MAX, 8843, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10719, 10721, 8844, UINT16_MAX, 8844, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10723, 10725, 8845, UINT16_MAX, 8845, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10727, 10729, 8846, UINT16_MAX, 8846, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10731, 10733, 8847, UINT16_MAX, 8847, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10735, 10737, 8848, UINT16_MAX, 8848, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10739, 10741, 8849, UINT16_MAX, 8849, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10743, 10745, 8850, UINT16_MAX, 8850, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10747, 10717, UINT16_MAX, 8851, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10749, 10721, UINT16_MAX, 8852, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10751, 10725, UINT16_MAX, 8853, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10753, 10729, UINT16_MAX, 8854, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10755, 10733, UINT16_MAX, 8855, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10757, 10737, UINT16_MAX, 8856, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10759, 10741, UINT16_MAX, 8857, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10761, 10745, UINT16_MAX, 8858, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10763, UINT16_MAX, 8859, UINT16_MAX, 8859, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10765, UINT16_MAX, 8860, UINT16_MAX, 8860, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10767, 10769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10771, 10773, 8861, UINT16_MAX, 8861, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10775, 10777, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10779, 10779, UINT16_MAX, UINT16_MAX, UINT16_MAX, 4907, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10781, 18975, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10786, 2596, UINT16_MAX, 2596, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10789, 2599, UINT16_MAX, 2599, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10792, 2602, UINT16_MAX, 2602, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2603, 2604, UINT16_MAX, 2604, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10797, 10773, UINT16_MAX, 8862, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 807, 807, 7217, UINT16_MAX, 7217, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 4919, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10805, 10807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10809, 10811, 8863, UINT16_MAX, 8863, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10813, 10815, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10817, 10817, UINT16_MAX, UINT16_MAX, UINT16_MAX, 4916, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10819, 19013, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10824, 2634, UINT16_MAX, 2634, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2635, 2636, UINT16_MAX, 2636, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10829, 2639, UINT16_MAX, 2639, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2640, 2641, UINT16_MAX, 2641, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10834, 10811, UINT16_MAX, 8864, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10836, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10838, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10840, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10842, UINT16_MAX, 8865, UINT16_MAX, 8865, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10844, UINT16_MAX, 8866, UINT16_MAX, 8866, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10846, 19040, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 2659, 17226, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10852, 10852, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10854, 19048, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10859, 2669, UINT16_MAX, 2669, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10862, 2672, UINT16_MAX, 2672, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10865, 2675, UINT16_MAX, 2675, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2676, 2677, UINT16_MAX, 2677, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10870, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10872, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10874, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10876, UINT16_MAX, 8867, UINT16_MAX, 8867, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10878, UINT16_MAX, 8868, UINT16_MAX, 8868, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10880, 19074, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 2693, 17267, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10886, 10886, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10888, UINT16_MAX, 8869, UINT16_MAX, 8869, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10890, 10890, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10892, 19086, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10897, 2707, UINT16_MAX, 2707, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10900, 2710, UINT16_MAX, 2710, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10903, 2713, UINT16_MAX, 2713, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2714, 2715, UINT16_MAX, 2715, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10908, 2718, UINT16_MAX, 2718, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 10911, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 2721, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 2722, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10915, 10917, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10919, 10921, 8870, UINT16_MAX, 8870, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10923, 10925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10927, 10927, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5036, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, 10929, 19123, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10934, 2744, UINT16_MAX, 2744, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2745, 2746, UINT16_MAX, 2746, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 10939, 2749, UINT16_MAX, 2749, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2750, 2751, UINT16_MAX, 2751, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LT, 0, UTF8PROC_BIDI_CLASS_L, 0, 10944, 10921, UINT16_MAX, 8871, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, 2754, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10947, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 4971, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, 0, 2757, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, 0, 2758, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, UTF8PROC_DECOMP_TYPE_COMPAT, 26, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, UTF8PROC_DECOMP_TYPE_NOBREAK, 26, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_BN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_ZWJ}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NOBREAK, 2759, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10952, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PI, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PF, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 2762, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10955, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19149, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZL, 0, UTF8PROC_BIDI_CLASS_WS, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_ZP, 0, UTF8PROC_BIDI_CLASS_B, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_LRE, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_RLE, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_PDF, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_LRO, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_RLO, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_COMPAT, 10960, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_COMPAT, 19154, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10965, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19159, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10970, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10972, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_CS, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10974, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10976, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 10978, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27364, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_LRI, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_RLI, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_FSI, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_PDI, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 8, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUPER, 2798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUPER, 2799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUPER, 2800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 2801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 2802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 2803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 13, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 38, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 31, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 32, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_SUB, 2798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUB, 2799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SUB, 2800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUB, 2801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUB, 2802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUB, 2803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 4, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 14, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 23, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 485, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 7, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 10, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 11, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 12, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 13, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 15, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 18, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 19, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_COMPAT, 10996, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19190, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19193, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2812, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11005, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19199, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19202, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2821, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11014, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 6, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 277, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 11, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11016, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2826, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 11019, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19213, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 11024, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2834, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2835, 866, UINT16_MAX, 866, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 1501, 10, UINT16_MAX, 10, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, 2836, 65, UINT16_MAX, 65, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 4, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2837, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 2838, UINT16_MAX, 2838, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 14, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2839, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2840, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2841, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2842, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 8, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19227, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 858, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 847, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2846, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2847, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FONT, 2848, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1494, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 3, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 8, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 9, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8872, UINT16_MAX, 8872, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19233, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19236, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 27431, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19243, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19246, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19249, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19252, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19255, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19258, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19261, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19264, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19267, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19270, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19273, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 11087, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 1499, 2897, UINT16_MAX, 2897, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11090, 2900, UINT16_MAX, 2900, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19285, 2904, UINT16_MAX, 2904, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11097, 2907, UINT16_MAX, 2907, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2908, 2909, UINT16_MAX, 2909, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11102, 2912, UINT16_MAX, 2912, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19297, 2916, UINT16_MAX, 2916, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 27493, 2921, UINT16_MAX, 2921, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11114, 2924, UINT16_MAX, 2924, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2925, 2926, UINT16_MAX, 2926, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11119, 2929, UINT16_MAX, 2929, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19314, 2933, UINT16_MAX, 2933, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 1502, 2934, UINT16_MAX, 2934, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2812, 2935, UINT16_MAX, 2935, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 1494, 2936, UINT16_MAX, 2936, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 1503, 2937, UINT16_MAX, 2937, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 8, UINT16_MAX, 8873, UINT16_MAX, 8873, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11130, UINT16_MAX, 8874, UINT16_MAX, 8874, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19324, UINT16_MAX, 8875, UINT16_MAX, 8875, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11135, UINT16_MAX, 8876, UINT16_MAX, 8876, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21, UINT16_MAX, 8877, UINT16_MAX, 8877, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11137, UINT16_MAX, 8878, UINT16_MAX, 8878, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19331, UINT16_MAX, 8879, UINT16_MAX, 8879, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 27526, UINT16_MAX, 8880, UINT16_MAX, 8880, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11146, UINT16_MAX, 8881, UINT16_MAX, 8881, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23, UINT16_MAX, 8882, UINT16_MAX, 8882, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11148, UINT16_MAX, 8883, UINT16_MAX, 8883, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19342, UINT16_MAX, 8884, UINT16_MAX, 8884, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 11, UINT16_MAX, 8885, UINT16_MAX, 8885, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 2, UINT16_MAX, 8886, UINT16_MAX, 8886, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3, UINT16_MAX, 8887, UINT16_MAX, 8887, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12, UINT16_MAX, 8888, UINT16_MAX, 8888, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 2961, UINT16_MAX, 2961, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8889, UINT16_MAX, 8889, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FRACTION, 19346, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5039, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5042, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5045, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11157, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11159, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11161, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11163, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11165, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11167, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5048, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5054, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5051, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5057, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11169, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5060, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11171, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5063, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11173, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5066, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11175, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5069, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11177, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11179, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19373, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11184, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19378, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5072, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11189, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5075, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11191, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5078, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11193, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5081, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11195, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5090, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11197, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5087, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11199, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5099, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5102, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11201, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11203, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11205, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11207, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11209, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5105, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5108, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11211, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11213, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5111, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5114, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11215, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11217, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5117, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5120, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5147, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5150, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11219, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11221, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5123, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5126, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11223, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11225, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5129, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5132, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11227, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11229, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5153, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5156, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5135, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5138, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5141, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5144, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11231, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11233, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11235, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11237, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5159, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5162, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5165, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5168, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11239, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11241, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11243, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11245, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11247, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11249, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11251, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11253, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, 3063, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, 3064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 38, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 31, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 32, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11257, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11259, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11261, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11263, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11265, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11267, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11269, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11271, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11273, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11275, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 11277, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19471, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19474, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19477, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19480, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19483, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19486, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19489, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19492, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27690, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27694, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27698, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27702, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27706, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27710, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27714, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27718, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27722, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27726, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27730, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11350, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11352, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11354, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11356, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11358, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11360, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11362, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11364, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 11366, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19560, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19563, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19566, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19569, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19572, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19578, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 19590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19605, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19608, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19611, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19614, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19617, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19620, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19623, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19626, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19629, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19632, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19635, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19638, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19641, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19644, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19647, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19650, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19653, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19656, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19659, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19662, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19665, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 19668, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1491, 3287, UINT16_MAX, 3287, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1493, 3288, UINT16_MAX, 3288, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2812, 3289, UINT16_MAX, 3289, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1494, 3290, UINT16_MAX, 3290, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1495, 3291, UINT16_MAX, 3291, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2837, 3292, UINT16_MAX, 3292, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1497, 3293, UINT16_MAX, 3293, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1498, 3294, UINT16_MAX, 3294, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1499, 3295, UINT16_MAX, 3295, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1500, 3296, UINT16_MAX, 3296, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1501, 3297, UINT16_MAX, 3297, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1502, 3298, UINT16_MAX, 3298, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1503, 3299, UINT16_MAX, 3299, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1504, 3300, UINT16_MAX, 3300, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1505, 3301, UINT16_MAX, 3301, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1507, 3302, UINT16_MAX, 3302, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2826, 3303, UINT16_MAX, 3303, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1508, 3304, UINT16_MAX, 3304, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3305, 3306, UINT16_MAX, 3306, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1509, 3307, UINT16_MAX, 3307, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1510, 3308, UINT16_MAX, 3308, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2908, 3309, UINT16_MAX, 3309, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1511, 3310, UINT16_MAX, 3310, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2925, 3311, UINT16_MAX, 3311, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3312, 3313, UINT16_MAX, 3313, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2834, 3314, UINT16_MAX, 3314, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 0, UINT16_MAX, 8890, UINT16_MAX, 8890, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1, UINT16_MAX, 8891, UINT16_MAX, 8891, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2, UINT16_MAX, 8892, UINT16_MAX, 8892, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3, UINT16_MAX, 8893, UINT16_MAX, 8893, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4, UINT16_MAX, 8894, UINT16_MAX, 8894, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 5, UINT16_MAX, 8895, UINT16_MAX, 8895, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 6, UINT16_MAX, 8896, UINT16_MAX, 8896, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 7, UINT16_MAX, 8897, UINT16_MAX, 8897, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 8, UINT16_MAX, 8898, UINT16_MAX, 8898, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 9, UINT16_MAX, 8899, UINT16_MAX, 8899, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 10, UINT16_MAX, 8900, UINT16_MAX, 8900, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 11, UINT16_MAX, 8901, UINT16_MAX, 8901, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12, UINT16_MAX, 8902, UINT16_MAX, 8902, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 13, UINT16_MAX, 8903, UINT16_MAX, 8903, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 14, UINT16_MAX, 8904, UINT16_MAX, 8904, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 15, UINT16_MAX, 8905, UINT16_MAX, 8905, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 16, UINT16_MAX, 8906, UINT16_MAX, 8906, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 17, UINT16_MAX, 8907, UINT16_MAX, 8907, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 18, UINT16_MAX, 8908, UINT16_MAX, 8908, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 19, UINT16_MAX, 8909, UINT16_MAX, 8909, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 20, UINT16_MAX, 8910, UINT16_MAX, 8910, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 21, UINT16_MAX, 8911, UINT16_MAX, 8911, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 22, UINT16_MAX, 8912, UINT16_MAX, 8912, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 23, UINT16_MAX, 8913, UINT16_MAX, 8913, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 24, UINT16_MAX, 8914, UINT16_MAX, 8914, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 25, UINT16_MAX, 8915, UINT16_MAX, 8915, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 2792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 27891, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19703, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 19708, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, 11519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5171, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3329, UINT16_MAX, 3329, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3330, UINT16_MAX, 3330, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3331, UINT16_MAX, 3331, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3332, UINT16_MAX, 3332, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3333, UINT16_MAX, 3333, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3334, UINT16_MAX, 3334, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3335, UINT16_MAX, 3335, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3336, UINT16_MAX, 3336, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3337, UINT16_MAX, 3337, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3338, UINT16_MAX, 3338, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3339, UINT16_MAX, 3339, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3340, UINT16_MAX, 3340, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3341, UINT16_MAX, 3341, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3342, UINT16_MAX, 3342, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3343, UINT16_MAX, 3343, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3344, UINT16_MAX, 3344, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3345, UINT16_MAX, 3345, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3346, UINT16_MAX, 3346, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3347, UINT16_MAX, 3347, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3348, UINT16_MAX, 3348, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3349, UINT16_MAX, 3349, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3350, UINT16_MAX, 3350, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3351, UINT16_MAX, 3351, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3352, UINT16_MAX, 3352, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3353, UINT16_MAX, 3353, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3354, UINT16_MAX, 3354, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3355, UINT16_MAX, 3355, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3356, UINT16_MAX, 3356, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3357, UINT16_MAX, 3357, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3358, UINT16_MAX, 3358, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3359, UINT16_MAX, 3359, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3360, UINT16_MAX, 3360, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3361, UINT16_MAX, 3361, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3362, UINT16_MAX, 3362, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3363, UINT16_MAX, 3363, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3364, UINT16_MAX, 3364, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3365, UINT16_MAX, 3365, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3366, UINT16_MAX, 3366, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3367, UINT16_MAX, 3367, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3368, UINT16_MAX, 3368, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3369, UINT16_MAX, 3369, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3370, UINT16_MAX, 3370, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3371, UINT16_MAX, 3371, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3372, UINT16_MAX, 3372, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3373, UINT16_MAX, 3373, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3374, UINT16_MAX, 3374, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3375, UINT16_MAX, 3375, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8916, UINT16_MAX, 8916, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8917, UINT16_MAX, 8917, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8918, UINT16_MAX, 8918, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8919, UINT16_MAX, 8919, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8920, UINT16_MAX, 8920, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8921, UINT16_MAX, 8921, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8922, UINT16_MAX, 8922, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8923, UINT16_MAX, 8923, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8924, UINT16_MAX, 8924, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8925, UINT16_MAX, 8925, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8926, UINT16_MAX, 8926, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8927, UINT16_MAX, 8927, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8928, UINT16_MAX, 8928, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8929, UINT16_MAX, 8929, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8930, UINT16_MAX, 8930, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8931, UINT16_MAX, 8931, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8932, UINT16_MAX, 8932, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8933, UINT16_MAX, 8933, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8934, UINT16_MAX, 8934, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8935, UINT16_MAX, 8935, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8936, UINT16_MAX, 8936, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8937, UINT16_MAX, 8937, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8938, UINT16_MAX, 8938, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8939, UINT16_MAX, 8939, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8940, UINT16_MAX, 8940, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8941, UINT16_MAX, 8941, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8942, UINT16_MAX, 8942, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8943, UINT16_MAX, 8943, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8944, UINT16_MAX, 8944, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8945, UINT16_MAX, 8945, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8946, UINT16_MAX, 8946, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8947, UINT16_MAX, 8947, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8948, UINT16_MAX, 8948, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8949, UINT16_MAX, 8949, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8950, UINT16_MAX, 8950, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8951, UINT16_MAX, 8951, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8952, UINT16_MAX, 8952, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8953, UINT16_MAX, 8953, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8954, UINT16_MAX, 8954, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8955, UINT16_MAX, 8955, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8956, UINT16_MAX, 8956, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8957, UINT16_MAX, 8957, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8958, UINT16_MAX, 8958, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8959, UINT16_MAX, 8959, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8960, UINT16_MAX, 8960, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8961, UINT16_MAX, 8961, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8962, UINT16_MAX, 8962, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3376, UINT16_MAX, 3376, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8963, UINT16_MAX, 8963, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3377, UINT16_MAX, 3377, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3378, UINT16_MAX, 3378, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3379, UINT16_MAX, 3379, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8964, UINT16_MAX, 8964, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8965, UINT16_MAX, 8965, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3380, UINT16_MAX, 3380, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8966, UINT16_MAX, 8966, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3381, UINT16_MAX, 3381, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8967, UINT16_MAX, 8967, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3382, UINT16_MAX, 3382, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8968, UINT16_MAX, 8968, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1513, UINT16_MAX, 1513, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1531, UINT16_MAX, 1531, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1512, UINT16_MAX, 1512, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1520, UINT16_MAX, 1520, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3383, UINT16_MAX, 3383, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8969, UINT16_MAX, 8969, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3384, UINT16_MAX, 3384, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8970, UINT16_MAX, 8970, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUB, 9, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 2908, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3385, UINT16_MAX, 3385, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3386, UINT16_MAX, 3386, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3387, UINT16_MAX, 3387, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8971, UINT16_MAX, 8971, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3388, UINT16_MAX, 3388, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8972, UINT16_MAX, 8972, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3389, UINT16_MAX, 3389, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8973, UINT16_MAX, 8973, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3390, UINT16_MAX, 3390, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8974, UINT16_MAX, 8974, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3391, UINT16_MAX, 3391, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8975, UINT16_MAX, 8975, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3392, UINT16_MAX, 3392, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8976, UINT16_MAX, 8976, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3393, UINT16_MAX, 3393, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8977, UINT16_MAX, 8977, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3394, UINT16_MAX, 3394, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8978, UINT16_MAX, 8978, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3395, UINT16_MAX, 3395, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8979, UINT16_MAX, 8979, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3396, UINT16_MAX, 3396, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8980, UINT16_MAX, 8980, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3397, UINT16_MAX, 3397, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8981, UINT16_MAX, 8981, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3398, UINT16_MAX, 3398, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8982, UINT16_MAX, 8982, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3399, UINT16_MAX, 3399, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8983, UINT16_MAX, 8983, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3400, UINT16_MAX, 3400, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8984, UINT16_MAX, 8984, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3401, UINT16_MAX, 3401, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8985, UINT16_MAX, 8985, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3402, UINT16_MAX, 3402, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8986, UINT16_MAX, 8986, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3403, UINT16_MAX, 3403, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8987, UINT16_MAX, 8987, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3404, UINT16_MAX, 3404, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8988, UINT16_MAX, 8988, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3405, UINT16_MAX, 3405, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8989, UINT16_MAX, 8989, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3406, UINT16_MAX, 3406, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8990, UINT16_MAX, 8990, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3407, UINT16_MAX, 3407, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8991, UINT16_MAX, 8991, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3408, UINT16_MAX, 3408, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8992, UINT16_MAX, 8992, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3409, UINT16_MAX, 3409, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8993, UINT16_MAX, 8993, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3410, UINT16_MAX, 3410, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8994, UINT16_MAX, 8994, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3411, UINT16_MAX, 3411, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8995, UINT16_MAX, 8995, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3412, UINT16_MAX, 3412, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8996, UINT16_MAX, 8996, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3413, UINT16_MAX, 3413, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8997, UINT16_MAX, 8997, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3414, UINT16_MAX, 3414, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8998, UINT16_MAX, 8998, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3415, UINT16_MAX, 3415, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8999, UINT16_MAX, 8999, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3416, UINT16_MAX, 3416, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9000, UINT16_MAX, 9000, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3417, UINT16_MAX, 3417, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9001, UINT16_MAX, 9001, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3418, UINT16_MAX, 3418, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9002, UINT16_MAX, 9002, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3419, UINT16_MAX, 3419, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9003, UINT16_MAX, 9003, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3420, UINT16_MAX, 3420, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9004, UINT16_MAX, 9004, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3421, UINT16_MAX, 3421, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9005, UINT16_MAX, 9005, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3422, UINT16_MAX, 3422, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9006, UINT16_MAX, 9006, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3423, UINT16_MAX, 3423, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9007, UINT16_MAX, 9007, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3424, UINT16_MAX, 3424, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9008, UINT16_MAX, 9008, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3425, UINT16_MAX, 3425, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9009, UINT16_MAX, 9009, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3426, UINT16_MAX, 3426, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9010, UINT16_MAX, 9010, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3427, UINT16_MAX, 3427, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9011, UINT16_MAX, 9011, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3428, UINT16_MAX, 3428, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9012, UINT16_MAX, 9012, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3429, UINT16_MAX, 3429, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9013, UINT16_MAX, 9013, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3430, UINT16_MAX, 3430, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9014, UINT16_MAX, 9014, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3431, UINT16_MAX, 3431, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9015, UINT16_MAX, 9015, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3432, UINT16_MAX, 3432, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9016, UINT16_MAX, 9016, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3433, UINT16_MAX, 3433, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9017, UINT16_MAX, 9017, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3434, UINT16_MAX, 3434, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9018, UINT16_MAX, 9018, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3435, UINT16_MAX, 3435, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9019, UINT16_MAX, 9019, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3436, UINT16_MAX, 3436, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9020, UINT16_MAX, 9020, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3437, UINT16_MAX, 3437, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9021, UINT16_MAX, 9021, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3438, UINT16_MAX, 3438, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9022, UINT16_MAX, 9022, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 3439, UINT16_MAX, 3439, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9023, UINT16_MAX, 9023, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9024, UINT16_MAX, 9024, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9025, UINT16_MAX, 9025, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9026, UINT16_MAX, 9026, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9027, UINT16_MAX, 9027, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9028, UINT16_MAX, 9028, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9029, UINT16_MAX, 9029, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9030, UINT16_MAX, 9030, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9031, UINT16_MAX, 9031, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9032, UINT16_MAX, 9032, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9033, UINT16_MAX, 9033, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9034, UINT16_MAX, 9034, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9035, UINT16_MAX, 9035, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9036, UINT16_MAX, 9036, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9037, UINT16_MAX, 9037, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9038, UINT16_MAX, 9038, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9039, UINT16_MAX, 9039, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9040, UINT16_MAX, 9040, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9041, UINT16_MAX, 9041, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9042, UINT16_MAX, 9042, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9043, UINT16_MAX, 9043, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9044, UINT16_MAX, 9044, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9045, UINT16_MAX, 9045, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9046, UINT16_MAX, 9046, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9047, UINT16_MAX, 9047, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9048, UINT16_MAX, 9048, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9049, UINT16_MAX, 9049, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9050, UINT16_MAX, 9050, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9051, UINT16_MAX, 9051, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9052, UINT16_MAX, 9052, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9053, UINT16_MAX, 9053, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9054, UINT16_MAX, 9054, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9055, UINT16_MAX, 9055, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9056, UINT16_MAX, 9056, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9057, UINT16_MAX, 9057, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9058, UINT16_MAX, 9058, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9059, UINT16_MAX, 9059, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9060, UINT16_MAX, 9060, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9061, UINT16_MAX, 9061, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9062, UINT16_MAX, 9062, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9063, UINT16_MAX, 9063, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3440, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3441, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3442, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3444, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3445, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3446, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3447, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3448, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3450, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3451, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3452, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3453, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3454, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3455, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3456, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3457, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3458, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3459, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3460, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3461, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3462, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3463, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3464, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3465, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3466, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3467, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3468, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3469, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3470, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3471, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3472, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3473, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3474, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3475, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3476, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3477, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3478, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3479, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3480, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3481, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3482, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3483, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3484, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3485, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3486, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3487, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3488, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3489, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3490, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3492, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3494, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3496, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3500, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3512, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3513, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3515, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3521, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3522, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3527, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3528, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3529, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3531, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3533, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3535, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3539, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3541, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3542, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3543, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3544, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3545, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3546, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3547, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3548, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3549, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3550, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3551, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3552, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3553, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3554, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3555, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3556, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3557, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3559, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3560, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3561, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3562, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3563, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3564, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3565, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3566, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3567, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3568, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3569, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3570, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3571, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3572, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3573, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3574, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3578, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3605, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3606, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3607, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3608, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3610, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3611, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3612, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3613, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3614, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3615, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3616, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3617, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3618, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3619, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3620, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3621, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3622, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3623, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3624, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3625, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3626, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3627, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3628, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3629, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3630, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3631, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3632, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3633, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3634, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3635, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3636, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3637, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3638, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3639, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3640, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3641, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3642, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3643, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3644, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3645, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3646, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3647, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3648, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3649, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3650, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3651, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3652, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3653, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3655, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3656, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ZS, 0, UTF8PROC_BIDI_CLASS_WS, UTF8PROC_DECOMP_TYPE_WIDE, 26, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 218, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 224, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 3657, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3466, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3658, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3659, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5239, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5174, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11852, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5177, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11854, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5180, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11856, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5183, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11858, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5186, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11860, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5189, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11862, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5192, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11864, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5195, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11866, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5198, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11868, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5201, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11870, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5204, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11872, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5207, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11874, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5210, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11876, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5213, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11878, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5216, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11880, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5219, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11882, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11884, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5223, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11886, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11888, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5227, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11890, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11892, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5231, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11894, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11896, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5235, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11898, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11900, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11902, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 8, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32820, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 8, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 32821, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11904, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 11906, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5242, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, 11908, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_VERTICAL, 11910, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5310, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5245, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11912, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5248, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11914, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5251, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11916, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5254, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11918, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5257, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11920, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5260, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11922, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5263, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11924, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5266, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11926, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5269, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11928, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5272, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11930, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5275, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11932, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5278, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11934, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5281, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11936, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5284, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11938, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5287, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11940, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5290, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11942, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11944, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5294, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11946, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11948, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5298, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11950, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11952, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5302, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11954, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11956, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5306, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11958, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11960, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5313, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5316, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5319, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5322, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11962, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11964, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11966, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11968, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 11970, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5325, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, 0, 11972, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_VERTICAL, 11974, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3784, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3788, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3790, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3791, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3804, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3805, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3806, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3808, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3809, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3810, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3811, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3812, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3813, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3815, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3816, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3817, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3818, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3819, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3820, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3821, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3822, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3823, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3824, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3825, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3826, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3827, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3828, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3829, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3830, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3831, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3832, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3833, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3834, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3835, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3836, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3837, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3838, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3839, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3840, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3841, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3842, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3843, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3844, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3845, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3846, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3847, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3848, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3849, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3850, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3851, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3852, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3853, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3854, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3855, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3856, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3857, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3858, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3859, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3860, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3861, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3862, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3863, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3864, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3865, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3866, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3867, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3868, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3869, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3870, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3871, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3872, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3873, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3874, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3875, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3876, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 3877, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3878, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3879, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3880, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3881, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3882, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3883, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3447, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3884, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3885, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3886, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3887, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3451, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20272, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20275, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20278, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20281, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20284, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20287, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20290, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20296, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20299, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20302, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20308, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28522, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28542, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28546, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28550, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28554, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 28562, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 53142, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 44957, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20387, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20390, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20393, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20396, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20399, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20402, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20405, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20408, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20411, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20414, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20417, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20420, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20423, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20426, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20429, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20432, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20435, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20438, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20441, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20444, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20447, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20450, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20453, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20456, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20459, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20462, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20465, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20468, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20471, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20474, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20477, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20480, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20483, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20486, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20489, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20492, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4111, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4112, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4113, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 20498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12313, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12315, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12319, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12321, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12323, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12325, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12333, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12337, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3784, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3790, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3804, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3806, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3809, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3810, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3811, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3812, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3813, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12339, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12341, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12343, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12345, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12347, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12349, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12351, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12353, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12355, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12357, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12359, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12361, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12363, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 12365, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 36943, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 28756, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12376, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3878, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3879, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4186, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4187, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4188, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3454, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4189, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3466, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3528, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3527, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3474, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4190, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4191, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4192, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4193, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4194, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4195, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4196, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4197, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4198, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4199, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3480, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4200, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4201, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4202, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4203, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4204, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4205, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4206, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4207, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3880, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3881, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 3882, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4208, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4209, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4210, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4211, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4212, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4213, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4214, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4215, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4216, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4217, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12410, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12412, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12414, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12416, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12418, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12420, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12422, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12424, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12426, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12428, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12430, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12432, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12434, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12436, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_CIRCLE, 12438, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12440, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12442, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12444, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12446, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12448, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12450, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12452, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12454, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12456, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20650, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20653, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 20656, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 12467, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 20661, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 12472, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 20666, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4285, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4286, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4287, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4288, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4289, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4290, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4291, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4292, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4294, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4295, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4296, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4297, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4298, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4299, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4300, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4301, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4302, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4303, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4304, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4306, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4307, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4308, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4310, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4312, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4313, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4314, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4315, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4316, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4318, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4319, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4320, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4321, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4322, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4323, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4324, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4325, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4326, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4328, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4330, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 4331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28910, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28914, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28918, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20730, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20737, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20740, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37127, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28940, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20752, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20755, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20758, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28953, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28957, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20772, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20777, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28972, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 28976, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37174, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 45371, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37185, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20806, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37193, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37198, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29011, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20823, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20826, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20829, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29024, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37220, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29033, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20845, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20848, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20851, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12662, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12664, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12666, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12668, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20862, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20865, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37252, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20873, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29068, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37264, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20885, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12696, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12698, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29089, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37285, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20906, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12722, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20916, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20919, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20922, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20928, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29123, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20935, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12746, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20940, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20943, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20946, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29141, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20953, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20956, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20959, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37346, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29159, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12779, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37357, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29172, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29176, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20988, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20991, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 20994, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29189, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12809, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21003, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29198, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12818, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37396, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21017, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12828, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12830, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12832, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12834, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12836, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12838, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12840, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12842, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12844, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 12846, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21040, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21043, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21046, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21049, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21052, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21055, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21058, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21061, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21067, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21070, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21073, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21076, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21079, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21082, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21085, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12896, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12898, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21092, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12903, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12905, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 12907, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 21101, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 21104, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 12915, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12917, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12919, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12921, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12923, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12929, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12931, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12933, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12935, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12937, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12939, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12941, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12943, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21137, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29332, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12952, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12954, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12956, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12958, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12960, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12962, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12964, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21158, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21161, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21164, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21167, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12978, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12980, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12982, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12984, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12986, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12988, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12990, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12992, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12994, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 12996, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21190, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21193, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13004, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21198, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21201, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21204, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13015, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21209, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21212, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29407, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13027, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21221, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21224, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21227, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21230, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 37617, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 45814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13052, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13054, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13056, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13058, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13060, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13062, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13066, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13068, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13070, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13072, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13074, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13076, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13078, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13080, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13082, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13084, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13086, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29472, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13092, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13094, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13096, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29482, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21294, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13105, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13107, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13109, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13111, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13113, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13115, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13117, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13119, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13121, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13123, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13128, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13130, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21324, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13138, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 29524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 21336, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13147, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13149, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13151, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13153, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 21347, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 21350, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13161, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13163, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13165, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13167, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13169, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13171, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13173, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13175, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13177, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21371, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21374, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21377, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21380, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21383, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21386, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21389, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21392, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21395, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21398, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21401, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21404, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21407, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21410, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21413, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21416, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21419, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21422, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21425, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21428, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21431, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 21434, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SQUARE, 21437, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5056, UINT16_MAX, 5056, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9064, UINT16_MAX, 9064, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5057, UINT16_MAX, 5057, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9065, UINT16_MAX, 9065, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5058, UINT16_MAX, 5058, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9066, UINT16_MAX, 9066, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5059, UINT16_MAX, 5059, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9067, UINT16_MAX, 9067, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5060, UINT16_MAX, 5060, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9068, UINT16_MAX, 9068, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1445, UINT16_MAX, 1445, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 8621, UINT16_MAX, 8621, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5061, UINT16_MAX, 5061, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9069, UINT16_MAX, 9069, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5062, UINT16_MAX, 5062, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9070, UINT16_MAX, 9070, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5063, UINT16_MAX, 5063, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9071, UINT16_MAX, 9071, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5064, UINT16_MAX, 5064, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9072, UINT16_MAX, 9072, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5065, UINT16_MAX, 5065, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9073, UINT16_MAX, 9073, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5066, UINT16_MAX, 5066, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9074, UINT16_MAX, 9074, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5067, UINT16_MAX, 5067, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9075, UINT16_MAX, 9075, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5068, UINT16_MAX, 5068, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9076, UINT16_MAX, 9076, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5069, UINT16_MAX, 5069, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9077, UINT16_MAX, 9077, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5070, UINT16_MAX, 5070, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9078, UINT16_MAX, 9078, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5071, UINT16_MAX, 5071, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9079, UINT16_MAX, 9079, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5072, UINT16_MAX, 5072, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9080, UINT16_MAX, 9080, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5073, UINT16_MAX, 5073, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9081, UINT16_MAX, 9081, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5074, UINT16_MAX, 5074, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9082, UINT16_MAX, 9082, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5075, UINT16_MAX, 5075, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9083, UINT16_MAX, 9083, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5076, UINT16_MAX, 5076, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9084, UINT16_MAX, 9084, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5077, UINT16_MAX, 5077, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9085, UINT16_MAX, 9085, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5078, UINT16_MAX, 5078, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9086, UINT16_MAX, 9086, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5079, UINT16_MAX, 5079, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9087, UINT16_MAX, 9087, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5080, UINT16_MAX, 5080, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9088, UINT16_MAX, 9088, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5081, UINT16_MAX, 5081, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9089, UINT16_MAX, 9089, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5082, UINT16_MAX, 5082, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9090, UINT16_MAX, 9090, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5083, UINT16_MAX, 5083, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9091, UINT16_MAX, 9091, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5084, UINT16_MAX, 5084, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9092, UINT16_MAX, 9092, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5085, UINT16_MAX, 5085, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9093, UINT16_MAX, 9093, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5086, UINT16_MAX, 5086, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9094, UINT16_MAX, 9094, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5087, UINT16_MAX, 5087, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9095, UINT16_MAX, 9095, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5088, UINT16_MAX, 5088, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9096, UINT16_MAX, 9096, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5089, UINT16_MAX, 5089, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9097, UINT16_MAX, 9097, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5090, UINT16_MAX, 5090, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9098, UINT16_MAX, 9098, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5091, UINT16_MAX, 5091, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9099, UINT16_MAX, 9099, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 981, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 983, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5092, UINT16_MAX, 5092, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9100, UINT16_MAX, 9100, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5093, UINT16_MAX, 5093, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9101, UINT16_MAX, 9101, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5094, UINT16_MAX, 5094, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9102, UINT16_MAX, 9102, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5095, UINT16_MAX, 5095, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9103, UINT16_MAX, 9103, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5096, UINT16_MAX, 5096, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9104, UINT16_MAX, 9104, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5097, UINT16_MAX, 5097, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9105, UINT16_MAX, 9105, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5098, UINT16_MAX, 5098, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9106, UINT16_MAX, 9106, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5099, UINT16_MAX, 5099, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9107, UINT16_MAX, 9107, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5100, UINT16_MAX, 5100, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9108, UINT16_MAX, 9108, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5101, UINT16_MAX, 5101, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9109, UINT16_MAX, 9109, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5102, UINT16_MAX, 5102, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9110, UINT16_MAX, 9110, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5103, UINT16_MAX, 5103, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9111, UINT16_MAX, 9111, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5104, UINT16_MAX, 5104, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9112, UINT16_MAX, 9112, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5105, UINT16_MAX, 5105, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9113, UINT16_MAX, 9113, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5106, UINT16_MAX, 5106, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9114, UINT16_MAX, 9114, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5107, UINT16_MAX, 5107, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9115, UINT16_MAX, 9115, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5108, UINT16_MAX, 5108, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9116, UINT16_MAX, 9116, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5109, UINT16_MAX, 5109, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9117, UINT16_MAX, 9117, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5110, UINT16_MAX, 5110, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9118, UINT16_MAX, 9118, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5111, UINT16_MAX, 5111, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9119, UINT16_MAX, 9119, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5112, UINT16_MAX, 5112, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9120, UINT16_MAX, 9120, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5113, UINT16_MAX, 5113, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9121, UINT16_MAX, 9121, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5114, UINT16_MAX, 5114, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9122, UINT16_MAX, 9122, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5115, UINT16_MAX, 5115, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9123, UINT16_MAX, 9123, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5116, UINT16_MAX, 5116, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9124, UINT16_MAX, 9124, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5117, UINT16_MAX, 5117, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9125, UINT16_MAX, 9125, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5118, UINT16_MAX, 5118, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9126, UINT16_MAX, 9126, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5119, UINT16_MAX, 5119, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9127, UINT16_MAX, 9127, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5120, UINT16_MAX, 5120, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9128, UINT16_MAX, 9128, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5121, UINT16_MAX, 5121, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9129, UINT16_MAX, 9129, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5122, UINT16_MAX, 5122, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9130, UINT16_MAX, 9130, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5123, UINT16_MAX, 5123, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9131, UINT16_MAX, 9131, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5124, UINT16_MAX, 5124, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9132, UINT16_MAX, 9132, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5125, UINT16_MAX, 5125, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9133, UINT16_MAX, 9133, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5126, UINT16_MAX, 5126, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9134, UINT16_MAX, 9134, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5127, UINT16_MAX, 5127, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9135, UINT16_MAX, 9135, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5128, UINT16_MAX, 5128, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9136, UINT16_MAX, 9136, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5129, UINT16_MAX, 5129, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9137, UINT16_MAX, 9137, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 5129, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5130, UINT16_MAX, 5130, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9138, UINT16_MAX, 9138, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5131, UINT16_MAX, 5131, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9139, UINT16_MAX, 9139, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5132, UINT16_MAX, 5132, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5133, UINT16_MAX, 5133, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9140, UINT16_MAX, 9140, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5134, UINT16_MAX, 5134, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9141, UINT16_MAX, 9141, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5135, UINT16_MAX, 5135, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9142, UINT16_MAX, 9142, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5136, UINT16_MAX, 5136, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9143, UINT16_MAX, 9143, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5137, UINT16_MAX, 5137, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9144, UINT16_MAX, 9144, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5138, UINT16_MAX, 5138, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9145, UINT16_MAX, 9145, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1524, UINT16_MAX, 1524, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5139, UINT16_MAX, 5139, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9146, UINT16_MAX, 9146, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5140, UINT16_MAX, 5140, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9147, UINT16_MAX, 9147, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9148, UINT16_MAX, 9148, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5141, UINT16_MAX, 5141, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9149, UINT16_MAX, 9149, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5142, UINT16_MAX, 5142, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9150, UINT16_MAX, 9150, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5143, UINT16_MAX, 5143, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9151, UINT16_MAX, 9151, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5144, UINT16_MAX, 5144, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9152, UINT16_MAX, 9152, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5145, UINT16_MAX, 5145, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9153, UINT16_MAX, 9153, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5146, UINT16_MAX, 5146, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9154, UINT16_MAX, 9154, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5147, UINT16_MAX, 5147, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9155, UINT16_MAX, 9155, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5148, UINT16_MAX, 5148, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9156, UINT16_MAX, 9156, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5149, UINT16_MAX, 5149, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9157, UINT16_MAX, 9157, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5150, UINT16_MAX, 5150, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9158, UINT16_MAX, 9158, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 785, UINT16_MAX, 785, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1515, UINT16_MAX, 1515, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1523, UINT16_MAX, 1523, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5151, UINT16_MAX, 5151, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1525, UINT16_MAX, 1525, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5152, UINT16_MAX, 5152, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5153, UINT16_MAX, 5153, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1527, UINT16_MAX, 1527, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5154, UINT16_MAX, 5154, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5155, UINT16_MAX, 5155, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9159, UINT16_MAX, 9159, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5156, UINT16_MAX, 5156, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9160, UINT16_MAX, 9160, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5157, UINT16_MAX, 5157, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9161, UINT16_MAX, 9161, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5158, UINT16_MAX, 5158, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9162, UINT16_MAX, 9162, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5159, UINT16_MAX, 5159, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9163, UINT16_MAX, 9163, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5160, UINT16_MAX, 5160, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9164, UINT16_MAX, 9164, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5161, UINT16_MAX, 5161, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9165, UINT16_MAX, 9165, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5162, UINT16_MAX, 5162, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 1536, UINT16_MAX, 1536, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5163, UINT16_MAX, 5163, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 5164, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 371, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9166, UINT16_MAX, 9166, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 5094, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 5165, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 3377, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SUPER, 5166, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5167, 5167, UINT16_MAX, 5167, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5168, 5168, UINT16_MAX, 5168, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5169, 5169, UINT16_MAX, 5169, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5170, 5170, UINT16_MAX, 5170, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5171, 5171, UINT16_MAX, 5171, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5172, 5172, UINT16_MAX, 5172, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5173, 5173, UINT16_MAX, 5173, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5174, 5174, UINT16_MAX, 5174, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5175, 5175, UINT16_MAX, 5175, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5176, 5176, UINT16_MAX, 5176, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5177, 5177, UINT16_MAX, 5177, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5178, 5178, UINT16_MAX, 5178, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5179, 5179, UINT16_MAX, 5179, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5180, 5180, UINT16_MAX, 5180, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5181, 5181, UINT16_MAX, 5181, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5182, 5182, UINT16_MAX, 5182, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5183, 5183, UINT16_MAX, 5183, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5184, 5184, UINT16_MAX, 5184, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5185, 5185, UINT16_MAX, 5185, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5186, 5186, UINT16_MAX, 5186, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5187, 5187, UINT16_MAX, 5187, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5188, 5188, UINT16_MAX, 5188, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5189, 5189, UINT16_MAX, 5189, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5190, 5190, UINT16_MAX, 5190, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5191, 5191, UINT16_MAX, 5191, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5192, 5192, UINT16_MAX, 5192, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5193, 5193, UINT16_MAX, 5193, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5194, 5194, UINT16_MAX, 5194, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5195, 5195, UINT16_MAX, 5195, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5196, 5196, UINT16_MAX, 5196, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5197, 5197, UINT16_MAX, 5197, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5198, 5198, UINT16_MAX, 5198, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5199, 5199, UINT16_MAX, 5199, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5200, 5200, UINT16_MAX, 5200, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5201, 5201, UINT16_MAX, 5201, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5202, 5202, UINT16_MAX, 5202, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5203, 5203, UINT16_MAX, 5203, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5204, 5204, UINT16_MAX, 5204, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5205, 5205, UINT16_MAX, 5205, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5206, 5206, UINT16_MAX, 5206, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5207, 5207, UINT16_MAX, 5207, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5208, 5208, UINT16_MAX, 5208, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5209, 5209, UINT16_MAX, 5209, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5210, 5210, UINT16_MAX, 5210, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5211, 5211, UINT16_MAX, 5211, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5212, 5212, UINT16_MAX, 5212, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5213, 5213, UINT16_MAX, 5213, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5214, 5214, UINT16_MAX, 5214, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5215, 5215, UINT16_MAX, 5215, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5216, 5216, UINT16_MAX, 5216, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5217, 5217, UINT16_MAX, 5217, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5218, 5218, UINT16_MAX, 5218, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5219, 5219, UINT16_MAX, 5219, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5220, 5220, UINT16_MAX, 5220, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5221, 5221, UINT16_MAX, 5221, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5222, 5222, UINT16_MAX, 5222, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5223, 5223, UINT16_MAX, 5223, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5224, 5224, UINT16_MAX, 5224, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5225, 5225, UINT16_MAX, 5225, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5226, 5226, UINT16_MAX, 5226, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5227, 5227, UINT16_MAX, 5227, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5228, 5228, UINT16_MAX, 5228, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5229, 5229, UINT16_MAX, 5229, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5230, 5230, UINT16_MAX, 5230, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5231, 5231, UINT16_MAX, 5231, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5232, 5232, UINT16_MAX, 5232, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5233, 5233, UINT16_MAX, 5233, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5234, 5234, UINT16_MAX, 5234, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5235, 5235, UINT16_MAX, 5235, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5236, 5236, UINT16_MAX, 5236, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5237, 5237, UINT16_MAX, 5237, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5238, 5238, UINT16_MAX, 5238, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5239, 5239, UINT16_MAX, 5239, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5240, 5240, UINT16_MAX, 5240, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5241, 5241, UINT16_MAX, 5241, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5242, 5242, UINT16_MAX, 5242, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5243, 5243, UINT16_MAX, 5243, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5244, 5244, UINT16_MAX, 5244, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5245, 5245, UINT16_MAX, 5245, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 5246, 5246, UINT16_MAX, 5246, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_LV}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_LVT}, + {UTF8PROC_CATEGORY_CS, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5247, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5248, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5249, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5250, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5251, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5252, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3655, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5253, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5254, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5255, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5256, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5257, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5258, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5259, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5260, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5261, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5262, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5263, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5264, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5265, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5266, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5267, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5268, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5269, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5270, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5271, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5272, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5273, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5274, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5275, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5277, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5278, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5279, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5280, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5281, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5282, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5283, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5284, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5285, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5286, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5287, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5288, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5289, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5290, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5291, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5292, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5294, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3567, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5295, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5296, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5297, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5298, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5299, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5300, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5301, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5302, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5303, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5304, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3640, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5306, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5307, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5308, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5310, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5312, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5313, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5314, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5315, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5316, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5318, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5319, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5320, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5321, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5322, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5323, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5324, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5325, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5326, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5328, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5330, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5332, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5333, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5334, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5336, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5337, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5338, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5339, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5340, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5341, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5342, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5343, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5344, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5345, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5346, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5347, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5348, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5349, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5350, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5351, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5352, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5353, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5354, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5355, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5356, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5357, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5358, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5359, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5360, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5361, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5362, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5363, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5364, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5365, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5366, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5367, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3480, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5368, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5369, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5370, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5371, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5372, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5373, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5374, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5375, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3461, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5376, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5377, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5378, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5379, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5380, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5381, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5382, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5383, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5384, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5385, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5386, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5387, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5388, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5389, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5390, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5391, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5392, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5393, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5394, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5395, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5396, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5397, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5398, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5399, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5400, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5401, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5402, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5403, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5404, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5405, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5406, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5407, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5408, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5409, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5410, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5411, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5412, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5413, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5414, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5415, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5416, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5417, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5418, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5419, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5420, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5421, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5422, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5423, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5424, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5425, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5426, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5427, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5428, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5429, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5430, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5431, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5432, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5433, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5434, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5435, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5436, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5437, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5438, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5439, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5440, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5441, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 4187, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5442, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5444, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5445, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5446, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5447, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5448, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5450, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5451, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5452, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5453, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5454, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5455, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5456, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5457, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5458, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5459, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5460, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5461, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5462, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5463, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3608, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5464, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5465, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5466, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5467, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5468, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5469, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5470, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5471, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5472, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5473, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5474, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5475, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5476, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3559, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5477, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5478, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5479, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5480, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5481, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5482, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5483, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5484, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5485, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5486, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5487, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5488, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5489, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5490, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5492, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5494, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5496, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5500, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3566, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5512, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5513, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5515, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5521, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5522, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5527, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5528, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5529, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3487, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5531, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5533, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5535, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5539, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5541, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5542, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5543, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5544, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 4192, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5545, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5546, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5547, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5548, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 4196, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5549, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5550, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5551, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5552, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5553, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5554, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5555, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5556, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5557, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5559, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5560, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5561, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5562, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5563, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5564, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5565, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5566, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5567, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5568, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5569, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5570, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5571, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5572, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5574, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5578, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5605, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5606, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5607, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5608, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5610, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5611, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5612, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5613, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5614, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5615, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5616, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5617, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5618, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5619, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5620, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5621, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5622, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5623, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5624, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5625, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5626, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5627, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5628, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5629, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5630, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5631, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5632, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5633, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5634, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5635, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5636, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5637, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5638, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5639, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5640, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5641, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5642, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5643, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5645, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5647, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5649, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5650, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5651, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5652, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5656, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5658, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 5659, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13852, 13852, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13854, 13854, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13856, 13856, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 22050, 22050, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 22053, 22053, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13864, 13866, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13866, 13866, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13868, 13868, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13870, 13870, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13872, 13872, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13874, 13874, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 13876, 13876, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13878, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 26, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13880, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5690, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 2839, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 2842, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5691, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5692, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5693, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5694, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5695, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_FONT, 5696, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_FONT, 2799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13889, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13891, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13893, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13895, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13897, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13899, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13901, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13903, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13905, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13907, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13909, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13911, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13913, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13915, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13917, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13919, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13921, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13923, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13927, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13929, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13931, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13933, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13935, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13937, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13939, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13941, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13943, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13945, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13947, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13949, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, 0, 13951, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_R, UTF8PROC_DECOMP_TYPE_COMPAT, 13953, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5763, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5763, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5764, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5764, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5764, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5764, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5765, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5765, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5765, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5765, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5766, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5766, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5766, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5766, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5767, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5767, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5767, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5767, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5768, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5768, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5768, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5768, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5770, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5770, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5770, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5770, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5771, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5771, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5771, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5771, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5772, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5772, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5772, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5772, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5773, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5773, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5773, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5773, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5774, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5774, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5774, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5774, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5775, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5775, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5775, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5775, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5776, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5776, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5777, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5777, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5778, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5778, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5779, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5779, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5780, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5780, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5781, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5781, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5782, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5782, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5782, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5782, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5783, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5783, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5783, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5783, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5784, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5784, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5784, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5784, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5788, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5788, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5790, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5790, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5790, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5790, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5791, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5791, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 13995, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 13995, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 13997, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 13997, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 13999, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 13999, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14001, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14001, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14003, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14003, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14005, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14005, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14007, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14007, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14007, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14009, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14009, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14009, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5819, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5819, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 5819, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 5819, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14012, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14014, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14016, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14018, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14020, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14022, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14024, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14026, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14028, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14030, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14032, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14034, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14036, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14038, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14040, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14042, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14044, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14046, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14048, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14050, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14052, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14054, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14056, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14058, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14060, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14062, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14066, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14068, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14070, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14072, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14074, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14076, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14078, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14080, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14082, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14084, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14086, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14088, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14090, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14092, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14094, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14096, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14098, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14100, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14102, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14104, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14106, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14108, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14110, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14112, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14114, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14116, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14118, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14120, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14122, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14124, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14126, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14128, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14130, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14132, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14134, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14136, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14138, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14140, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14142, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14144, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14146, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14148, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14150, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14152, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14154, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14156, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14158, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14160, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14162, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14164, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14166, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14168, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14170, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14172, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14174, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14176, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14178, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14180, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14182, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14184, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14186, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14188, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14190, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14192, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14194, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14196, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22390, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22393, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22396, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22399, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22402, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22405, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14216, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14218, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14016, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14220, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14018, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14222, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14224, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14026, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14226, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14028, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14030, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14228, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14230, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14038, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14232, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14040, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14042, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14234, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14236, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14046, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14238, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14048, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14050, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14108, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14110, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14116, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14118, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14120, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14128, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14130, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14132, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14134, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14142, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14144, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14146, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14240, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14154, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14242, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14244, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14166, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14246, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14168, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14170, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14196, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14248, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14250, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14186, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14252, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14188, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14190, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14012, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14014, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14254, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14016, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14256, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14020, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14022, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14024, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14026, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14258, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14032, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14034, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14036, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14038, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14260, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14046, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14052, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14054, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14056, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14058, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14060, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14066, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14068, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14070, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14072, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14074, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14262, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14076, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14078, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14080, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14082, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14084, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14086, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14090, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14092, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14094, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14096, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14098, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14100, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14102, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14104, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14106, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14112, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14114, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14122, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14124, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14126, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14128, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14130, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14136, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14138, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14140, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14142, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14264, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14148, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14150, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14152, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14154, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14160, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14162, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14164, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14166, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14266, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14172, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14174, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14268, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14180, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14182, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14184, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14186, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14270, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14016, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14256, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14026, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14258, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14038, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14260, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14046, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14272, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14072, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14274, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14278, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14128, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14130, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14142, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14166, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14266, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14186, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14270, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 22472, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 22475, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 22478, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14289, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14291, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14295, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14297, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14299, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14301, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14303, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14307, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14313, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14315, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14319, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14321, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14323, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14325, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14333, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14337, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14339, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14341, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14289, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14291, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14295, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14297, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14299, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14301, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14303, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14307, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14313, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14315, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14319, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14321, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14323, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14325, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14333, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14335, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14337, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14339, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14341, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14333, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14276, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14274, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14278, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 14088, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14066, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14068, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14070, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14329, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14333, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14088, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14090, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14343, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14343, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22543, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22546, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22549, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22552, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22555, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22561, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22564, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22567, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22570, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22573, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22606, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22606, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22612, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22615, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22618, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22621, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22621, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22624, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22627, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22630, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22633, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22636, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22636, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22639, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22642, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22645, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22648, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22651, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22657, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22657, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22660, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22660, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22663, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22666, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22669, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22672, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22675, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22678, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22681, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22684, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22687, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22690, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22693, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22696, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22699, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22699, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22702, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22705, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22708, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22711, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22711, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22714, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22717, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22720, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22723, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22726, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22729, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22732, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22735, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22738, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22741, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22744, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22747, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22750, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22753, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22756, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22759, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22762, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22765, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22768, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22771, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22774, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22777, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22639, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22645, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22780, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22783, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22804, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22810, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22618, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 22588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22813, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 22816, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22819, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22822, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31017, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31021, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31025, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31029, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31033, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31037, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31041, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 22853, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 63816, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 63835, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 31076, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6512, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6513, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6515, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 2802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 2803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6521, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6522, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 3063, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 3064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6527, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6528, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6529, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_VERTICAL, 6530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 6531, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_COMPAT, 6516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_SMALL, 6504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_SMALL, 2762, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_SMALL, 6507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 2802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 2803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_SMALL, 6532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6533, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SMALL, 2799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_SMALL, 6535, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 2801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_SMALL, 6539, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_SMALL, 6540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SMALL, 6541, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14734, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14736, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14738, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14740, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14742, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14744, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14746, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14748, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14750, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14752, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14754, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14756, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14758, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 14760, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6570, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6571, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6571, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6572, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6572, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6573, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6573, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6574, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6574, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6578, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6578, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 5802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 5802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 6604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 6604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_INITIAL, 6604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_MEDIAL, 6604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_ISOLATED, 14803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FINAL, 14803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6613, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6539, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6533, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6614, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 2802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 2803, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_WIDE, 2799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, 6504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PD, 0, UTF8PROC_BIDI_CLASS_ES, UTF8PROC_DECOMP_TYPE_WIDE, 6535, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, 2762, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, 6615, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 38, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 31, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 32, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_WIDE, 2798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_CS, UTF8PROC_DECOMP_TYPE_WIDE, 6507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 2801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6541, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1491, 6616, UINT16_MAX, 6616, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1493, 6617, UINT16_MAX, 6617, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2812, 6618, UINT16_MAX, 6618, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1494, 6619, UINT16_MAX, 6619, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1495, 6620, UINT16_MAX, 6620, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2837, 6621, UINT16_MAX, 6621, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1497, 6622, UINT16_MAX, 6622, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1498, 6623, UINT16_MAX, 6623, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1499, 6624, UINT16_MAX, 6624, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1500, 6625, UINT16_MAX, 6625, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1501, 6626, UINT16_MAX, 6626, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1502, 6627, UINT16_MAX, 6627, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1503, 6628, UINT16_MAX, 6628, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1504, 6629, UINT16_MAX, 6629, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1505, 6630, UINT16_MAX, 6630, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1507, 6631, UINT16_MAX, 6631, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2826, 6632, UINT16_MAX, 6632, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1508, 6633, UINT16_MAX, 6633, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 3305, 6634, UINT16_MAX, 6634, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1509, 6635, UINT16_MAX, 6635, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1510, 6636, UINT16_MAX, 6636, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2908, 6637, UINT16_MAX, 6637, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1511, 6638, UINT16_MAX, 6638, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2925, 6639, UINT16_MAX, 6639, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 3312, 6640, UINT16_MAX, 6640, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2834, 6641, UINT16_MAX, 6641, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6529, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6642, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PC, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 2722, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 0, UINT16_MAX, 9167, UINT16_MAX, 9167, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 1, UINT16_MAX, 9168, UINT16_MAX, 9168, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 2, UINT16_MAX, 9169, UINT16_MAX, 9169, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 3, UINT16_MAX, 9170, UINT16_MAX, 9170, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 4, UINT16_MAX, 9171, UINT16_MAX, 9171, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 5, UINT16_MAX, 9172, UINT16_MAX, 9172, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 6, UINT16_MAX, 9173, UINT16_MAX, 9173, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 7, UINT16_MAX, 9174, UINT16_MAX, 9174, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 8, UINT16_MAX, 9175, UINT16_MAX, 9175, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 9, UINT16_MAX, 9176, UINT16_MAX, 9176, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 10, UINT16_MAX, 9177, UINT16_MAX, 9177, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 11, UINT16_MAX, 9178, UINT16_MAX, 9178, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 12, UINT16_MAX, 9179, UINT16_MAX, 9179, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 13, UINT16_MAX, 9180, UINT16_MAX, 9180, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 14, UINT16_MAX, 9181, UINT16_MAX, 9181, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 15, UINT16_MAX, 9182, UINT16_MAX, 9182, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 16, UINT16_MAX, 9183, UINT16_MAX, 9183, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 17, UINT16_MAX, 9184, UINT16_MAX, 9184, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 18, UINT16_MAX, 9185, UINT16_MAX, 9185, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 19, UINT16_MAX, 9186, UINT16_MAX, 9186, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 20, UINT16_MAX, 9187, UINT16_MAX, 9187, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 21, UINT16_MAX, 9188, UINT16_MAX, 9188, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 22, UINT16_MAX, 9189, UINT16_MAX, 9189, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 23, UINT16_MAX, 9190, UINT16_MAX, 9190, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 24, UINT16_MAX, 9191, UINT16_MAX, 9191, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_WIDE, 25, UINT16_MAX, 9192, UINT16_MAX, 9192, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6643, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6644, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6645, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6646, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PS, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PE, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_PO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6647, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4331, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6648, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6649, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6650, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6651, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6652, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6653, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6655, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6656, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6657, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4285, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4286, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4287, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4288, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4289, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4290, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4291, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4292, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4293, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4294, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4295, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4296, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4297, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4298, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4299, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4300, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4301, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4302, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4303, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4304, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4306, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4307, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4308, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4310, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4311, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4312, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4313, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4314, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4315, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4316, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4317, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4318, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4319, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4320, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4321, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4322, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4323, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4324, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4325, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4326, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4327, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 4328, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6658, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6659, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6660, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6661, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, true, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6662, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6663, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6664, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6665, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6666, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6667, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6668, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6669, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6670, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6671, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6672, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6673, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6674, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6675, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6676, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6677, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6678, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6679, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6680, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6681, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6682, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6683, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6684, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6685, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6686, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6687, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6688, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6689, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6690, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6691, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6692, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6693, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6694, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6695, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6696, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6697, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6698, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6699, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6700, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6701, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6702, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6703, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6704, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6705, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6706, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6707, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6708, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6709, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6710, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6711, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_NARROW, 6712, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6713, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6714, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6715, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6716, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_WIDE, 6717, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6718, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SC, 0, UTF8PROC_BIDI_CLASS_ET, UTF8PROC_DECOMP_TYPE_WIDE, 6719, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6720, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6721, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6722, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6723, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6724, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6725, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_NARROW, 6726, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_NL, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6727, UINT16_MAX, 6727, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6729, UINT16_MAX, 6729, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6731, UINT16_MAX, 6731, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6733, UINT16_MAX, 6733, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6735, UINT16_MAX, 6735, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6737, UINT16_MAX, 6737, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6739, UINT16_MAX, 6739, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6741, UINT16_MAX, 6741, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6743, UINT16_MAX, 6743, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6745, UINT16_MAX, 6745, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6747, UINT16_MAX, 6747, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6749, UINT16_MAX, 6749, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6751, UINT16_MAX, 6751, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6753, UINT16_MAX, 6753, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6755, UINT16_MAX, 6755, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6757, UINT16_MAX, 6757, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6759, UINT16_MAX, 6759, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6761, UINT16_MAX, 6761, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6763, UINT16_MAX, 6763, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6765, UINT16_MAX, 6765, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6767, UINT16_MAX, 6767, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6769, UINT16_MAX, 6769, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6771, UINT16_MAX, 6771, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6773, UINT16_MAX, 6773, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6775, UINT16_MAX, 6775, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6777, UINT16_MAX, 6777, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6779, UINT16_MAX, 6779, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6781, UINT16_MAX, 6781, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6783, UINT16_MAX, 6783, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6785, UINT16_MAX, 6785, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6787, UINT16_MAX, 6787, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6789, UINT16_MAX, 6789, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6791, UINT16_MAX, 6791, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6793, UINT16_MAX, 6793, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6795, UINT16_MAX, 6795, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6797, UINT16_MAX, 6797, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6799, UINT16_MAX, 6799, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6801, UINT16_MAX, 6801, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6803, UINT16_MAX, 6803, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6805, UINT16_MAX, 6805, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9193, UINT16_MAX, 9193, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9195, UINT16_MAX, 9195, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9197, UINT16_MAX, 9197, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9199, UINT16_MAX, 9199, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9201, UINT16_MAX, 9201, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9203, UINT16_MAX, 9203, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9205, UINT16_MAX, 9205, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9207, UINT16_MAX, 9207, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9209, UINT16_MAX, 9209, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9211, UINT16_MAX, 9211, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9213, UINT16_MAX, 9213, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9215, UINT16_MAX, 9215, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9217, UINT16_MAX, 9217, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9219, UINT16_MAX, 9219, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9221, UINT16_MAX, 9221, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9223, UINT16_MAX, 9223, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9225, UINT16_MAX, 9225, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9227, UINT16_MAX, 9227, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9229, UINT16_MAX, 9229, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9231, UINT16_MAX, 9231, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9233, UINT16_MAX, 9233, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9235, UINT16_MAX, 9235, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9237, UINT16_MAX, 9237, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9239, UINT16_MAX, 9239, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9241, UINT16_MAX, 9241, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9243, UINT16_MAX, 9243, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9245, UINT16_MAX, 9245, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9247, UINT16_MAX, 9247, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9249, UINT16_MAX, 9249, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9251, UINT16_MAX, 9251, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9253, UINT16_MAX, 9253, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9255, UINT16_MAX, 9255, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9257, UINT16_MAX, 9257, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9259, UINT16_MAX, 9259, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9261, UINT16_MAX, 9261, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9263, UINT16_MAX, 9263, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9265, UINT16_MAX, 9265, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9267, UINT16_MAX, 9267, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9269, UINT16_MAX, 9269, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9271, UINT16_MAX, 9271, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6807, UINT16_MAX, 6807, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6809, UINT16_MAX, 6809, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6811, UINT16_MAX, 6811, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6813, UINT16_MAX, 6813, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6815, UINT16_MAX, 6815, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6817, UINT16_MAX, 6817, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6819, UINT16_MAX, 6819, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6821, UINT16_MAX, 6821, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6823, UINT16_MAX, 6823, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6825, UINT16_MAX, 6825, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6827, UINT16_MAX, 6827, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6829, UINT16_MAX, 6829, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6831, UINT16_MAX, 6831, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6833, UINT16_MAX, 6833, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6835, UINT16_MAX, 6835, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6837, UINT16_MAX, 6837, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6839, UINT16_MAX, 6839, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6841, UINT16_MAX, 6841, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6843, UINT16_MAX, 6843, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6845, UINT16_MAX, 6845, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6847, UINT16_MAX, 6847, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6849, UINT16_MAX, 6849, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6851, UINT16_MAX, 6851, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6853, UINT16_MAX, 6853, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6855, UINT16_MAX, 6855, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6857, UINT16_MAX, 6857, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6859, UINT16_MAX, 6859, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6861, UINT16_MAX, 6861, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6863, UINT16_MAX, 6863, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6865, UINT16_MAX, 6865, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6867, UINT16_MAX, 6867, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6869, UINT16_MAX, 6869, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6871, UINT16_MAX, 6871, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6873, UINT16_MAX, 6873, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6875, UINT16_MAX, 6875, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 6877, UINT16_MAX, 6877, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9273, UINT16_MAX, 9273, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9275, UINT16_MAX, 9275, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9277, UINT16_MAX, 9277, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9279, UINT16_MAX, 9279, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9281, UINT16_MAX, 9281, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9283, UINT16_MAX, 9283, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9285, UINT16_MAX, 9285, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9287, UINT16_MAX, 9287, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9289, UINT16_MAX, 9289, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9291, UINT16_MAX, 9291, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9293, UINT16_MAX, 9293, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9295, UINT16_MAX, 9295, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9297, UINT16_MAX, 9297, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9299, UINT16_MAX, 9299, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9301, UINT16_MAX, 9301, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9303, UINT16_MAX, 9303, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9305, UINT16_MAX, 9305, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9307, UINT16_MAX, 9307, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9309, UINT16_MAX, 9309, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9311, UINT16_MAX, 9311, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9313, UINT16_MAX, 9313, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9315, UINT16_MAX, 9315, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9317, UINT16_MAX, 9317, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9319, UINT16_MAX, 9319, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9321, UINT16_MAX, 9321, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9323, UINT16_MAX, 9323, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9325, UINT16_MAX, 9325, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9327, UINT16_MAX, 9327, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9329, UINT16_MAX, 9329, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9331, UINT16_MAX, 9331, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9333, UINT16_MAX, 9333, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9335, UINT16_MAX, 9335, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9337, UINT16_MAX, 9337, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9339, UINT16_MAX, 9339, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9341, UINT16_MAX, 9341, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9343, UINT16_MAX, 9343, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6879, UINT16_MAX, 6879, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6881, UINT16_MAX, 6881, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6883, UINT16_MAX, 6883, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6885, UINT16_MAX, 6885, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6887, UINT16_MAX, 6887, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6889, UINT16_MAX, 6889, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6891, UINT16_MAX, 6891, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6893, UINT16_MAX, 6893, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6895, UINT16_MAX, 6895, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6897, UINT16_MAX, 6897, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6899, UINT16_MAX, 6899, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6901, UINT16_MAX, 6901, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6903, UINT16_MAX, 6903, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6905, UINT16_MAX, 6905, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6907, UINT16_MAX, 6907, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6909, UINT16_MAX, 6909, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6911, UINT16_MAX, 6911, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6913, UINT16_MAX, 6913, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6915, UINT16_MAX, 6915, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6917, UINT16_MAX, 6917, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6919, UINT16_MAX, 6919, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6921, UINT16_MAX, 6921, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6923, UINT16_MAX, 6923, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6925, UINT16_MAX, 6925, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6927, UINT16_MAX, 6927, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6929, UINT16_MAX, 6929, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6931, UINT16_MAX, 6931, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6933, UINT16_MAX, 6933, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6935, UINT16_MAX, 6935, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6937, UINT16_MAX, 6937, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6939, UINT16_MAX, 6939, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6941, UINT16_MAX, 6941, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6943, UINT16_MAX, 6943, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6945, UINT16_MAX, 6945, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6947, UINT16_MAX, 6947, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6949, UINT16_MAX, 6949, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6951, UINT16_MAX, 6951, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6953, UINT16_MAX, 6953, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6955, UINT16_MAX, 6955, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6957, UINT16_MAX, 6957, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6959, UINT16_MAX, 6959, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6961, UINT16_MAX, 6961, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6963, UINT16_MAX, 6963, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6965, UINT16_MAX, 6965, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6967, UINT16_MAX, 6967, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6969, UINT16_MAX, 6969, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6971, UINT16_MAX, 6971, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6973, UINT16_MAX, 6973, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6975, UINT16_MAX, 6975, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6977, UINT16_MAX, 6977, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 6979, UINT16_MAX, 6979, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9345, UINT16_MAX, 9345, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9347, UINT16_MAX, 9347, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9349, UINT16_MAX, 9349, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9351, UINT16_MAX, 9351, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9353, UINT16_MAX, 9353, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9355, UINT16_MAX, 9355, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9357, UINT16_MAX, 9357, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9359, UINT16_MAX, 9359, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9361, UINT16_MAX, 9361, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9363, UINT16_MAX, 9363, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9365, UINT16_MAX, 9365, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9367, UINT16_MAX, 9367, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9369, UINT16_MAX, 9369, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9371, UINT16_MAX, 9371, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9373, UINT16_MAX, 9373, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9375, UINT16_MAX, 9375, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9377, UINT16_MAX, 9377, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9379, UINT16_MAX, 9379, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9381, UINT16_MAX, 9381, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9383, UINT16_MAX, 9383, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9385, UINT16_MAX, 9385, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9387, UINT16_MAX, 9387, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9389, UINT16_MAX, 9389, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9391, UINT16_MAX, 9391, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9393, UINT16_MAX, 9393, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9395, UINT16_MAX, 9395, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9397, UINT16_MAX, 9397, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9399, UINT16_MAX, 9399, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9401, UINT16_MAX, 9401, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9403, UINT16_MAX, 9403, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9405, UINT16_MAX, 9405, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9407, UINT16_MAX, 9407, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9409, UINT16_MAX, 9409, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9411, UINT16_MAX, 9411, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9413, UINT16_MAX, 9413, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9415, UINT16_MAX, 9415, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9417, UINT16_MAX, 9417, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9419, UINT16_MAX, 9419, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9421, UINT16_MAX, 9421, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9423, UINT16_MAX, 9423, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9425, UINT16_MAX, 9425, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9427, UINT16_MAX, 9427, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9429, UINT16_MAX, 9429, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9431, UINT16_MAX, 9431, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9433, UINT16_MAX, 9433, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9435, UINT16_MAX, 9435, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9437, UINT16_MAX, 9437, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9439, UINT16_MAX, 9439, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9441, UINT16_MAX, 9441, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9443, UINT16_MAX, 9443, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9445, UINT16_MAX, 9445, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_AN, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_AL, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5328, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15173, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5332, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15177, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5336, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15181, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 7, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49206, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_PREPEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49208, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 15185, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, 15189, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5340, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5344, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49210, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5348, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15193, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15197, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49212, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49216, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5354, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_NSM, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49214, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15201, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15205, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49218, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15209, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49220, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5362, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5366, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15213, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 0, UTF8PROC_BIDI_CLASS_L, 0, 15217, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7029, UINT16_MAX, 7029, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7031, UINT16_MAX, 7031, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7033, UINT16_MAX, 7033, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7035, UINT16_MAX, 7035, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7037, UINT16_MAX, 7037, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7039, UINT16_MAX, 7039, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7041, UINT16_MAX, 7041, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7043, UINT16_MAX, 7043, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7045, UINT16_MAX, 7045, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7047, UINT16_MAX, 7047, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7049, UINT16_MAX, 7049, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7051, UINT16_MAX, 7051, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7053, UINT16_MAX, 7053, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7055, UINT16_MAX, 7055, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7057, UINT16_MAX, 7057, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7059, UINT16_MAX, 7059, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7061, UINT16_MAX, 7061, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7063, UINT16_MAX, 7063, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7065, UINT16_MAX, 7065, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7067, UINT16_MAX, 7067, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7069, UINT16_MAX, 7069, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7071, UINT16_MAX, 7071, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7073, UINT16_MAX, 7073, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7075, UINT16_MAX, 7075, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7077, UINT16_MAX, 7077, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7079, UINT16_MAX, 7079, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7081, UINT16_MAX, 7081, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7083, UINT16_MAX, 7083, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7085, UINT16_MAX, 7085, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7087, UINT16_MAX, 7087, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7089, UINT16_MAX, 7089, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7091, UINT16_MAX, 7091, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9447, UINT16_MAX, 9447, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9449, UINT16_MAX, 9449, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9451, UINT16_MAX, 9451, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9453, UINT16_MAX, 9453, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9455, UINT16_MAX, 9455, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9457, UINT16_MAX, 9457, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9459, UINT16_MAX, 9459, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9461, UINT16_MAX, 9461, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9463, UINT16_MAX, 9463, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9465, UINT16_MAX, 9465, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9467, UINT16_MAX, 9467, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9469, UINT16_MAX, 9469, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9471, UINT16_MAX, 9471, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9473, UINT16_MAX, 9473, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9475, UINT16_MAX, 9475, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9477, UINT16_MAX, 9477, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9479, UINT16_MAX, 9479, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9481, UINT16_MAX, 9481, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9483, UINT16_MAX, 9483, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9485, UINT16_MAX, 9485, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9487, UINT16_MAX, 9487, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9489, UINT16_MAX, 9489, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9491, UINT16_MAX, 9491, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9493, UINT16_MAX, 9493, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9495, UINT16_MAX, 9495, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9497, UINT16_MAX, 9497, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9499, UINT16_MAX, 9499, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9501, UINT16_MAX, 9501, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9503, UINT16_MAX, 9503, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9505, UINT16_MAX, 9505, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9507, UINT16_MAX, 9507, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9509, UINT16_MAX, 9509, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MN, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MN, 9, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_CF, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, true, 0, 0, UTF8PROC_BOUNDCLASS_CONTROL}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7093, UINT16_MAX, 7093, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7095, UINT16_MAX, 7095, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7097, UINT16_MAX, 7097, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7099, UINT16_MAX, 7099, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7101, UINT16_MAX, 7101, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7103, UINT16_MAX, 7103, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7105, UINT16_MAX, 7105, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7107, UINT16_MAX, 7107, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7109, UINT16_MAX, 7109, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7111, UINT16_MAX, 7111, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7113, UINT16_MAX, 7113, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7115, UINT16_MAX, 7115, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7117, UINT16_MAX, 7117, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7119, UINT16_MAX, 7119, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7121, UINT16_MAX, 7121, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7123, UINT16_MAX, 7123, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7125, UINT16_MAX, 7125, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7127, UINT16_MAX, 7127, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7129, UINT16_MAX, 7129, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7131, UINT16_MAX, 7131, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7133, UINT16_MAX, 7133, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7135, UINT16_MAX, 7135, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7137, UINT16_MAX, 7137, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7139, UINT16_MAX, 7139, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7141, UINT16_MAX, 7141, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7143, UINT16_MAX, 7143, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7145, UINT16_MAX, 7145, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7147, UINT16_MAX, 7147, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7149, UINT16_MAX, 7149, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7151, UINT16_MAX, 7151, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7153, UINT16_MAX, 7153, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, 7155, UINT16_MAX, 7155, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9511, UINT16_MAX, 9511, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9513, UINT16_MAX, 9513, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9515, UINT16_MAX, 9515, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9517, UINT16_MAX, 9517, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9519, UINT16_MAX, 9519, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9521, UINT16_MAX, 9521, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9523, UINT16_MAX, 9523, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9525, UINT16_MAX, 9525, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9527, UINT16_MAX, 9527, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9529, UINT16_MAX, 9529, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9531, UINT16_MAX, 9531, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9533, UINT16_MAX, 9533, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9535, UINT16_MAX, 9535, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9537, UINT16_MAX, 9537, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9539, UINT16_MAX, 9539, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9541, UINT16_MAX, 9541, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9543, UINT16_MAX, 9543, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9545, UINT16_MAX, 9545, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9547, UINT16_MAX, 9547, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9549, UINT16_MAX, 9549, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9551, UINT16_MAX, 9551, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9553, UINT16_MAX, 9553, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9555, UINT16_MAX, 9555, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9557, UINT16_MAX, 9557, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9559, UINT16_MAX, 9559, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9561, UINT16_MAX, 9561, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9563, UINT16_MAX, 9563, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9565, UINT16_MAX, 9565, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9567, UINT16_MAX, 9567, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9569, UINT16_MAX, 9569, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9571, UINT16_MAX, 9571, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, 9573, UINT16_MAX, 9573, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5370, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5374, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15349, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15353, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5378, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15357, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15361, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15365, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15369, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15373, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49222, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 226, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_SPACINGMARK}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49224, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49226, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49228, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49230, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_MC, 216, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 49232, false, false, false, false, 0, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5390, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5394, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15377, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5398, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15381, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, 5404, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15385, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15389, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15393, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, 15397, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, true, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1500, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 3305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2908, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 3312, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 1, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 5, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 10, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 12, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 13, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 15, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 16, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 17, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 18, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 19, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 20, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 21, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 22, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 23, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 24, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 25, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7209, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7210, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7211, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7212, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7213, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7214, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7215, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7216, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 915, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7217, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7218, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7219, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7220, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7221, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7222, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7223, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7224, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7225, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 917, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7226, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 897, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7227, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7228, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7229, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 2835, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7230, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 845, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 846, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 848, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 849, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 850, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 851, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 852, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 853, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 854, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 35, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 855, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 856, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 857, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 859, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 914, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 860, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 861, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 862, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 863, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 864, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 865, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 866, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SM, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_FONT, 7231, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, true, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7232, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7233, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7234, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7235, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7236, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7237, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 7238, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_FONT, 904, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 38, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 31, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 32, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_ND, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_FONT, 2798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7239, UINT16_MAX, 7239, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7241, UINT16_MAX, 7241, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7243, UINT16_MAX, 7243, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7245, UINT16_MAX, 7245, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7247, UINT16_MAX, 7247, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7249, UINT16_MAX, 7249, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7251, UINT16_MAX, 7251, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7253, UINT16_MAX, 7253, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7255, UINT16_MAX, 7255, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7257, UINT16_MAX, 7257, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7259, UINT16_MAX, 7259, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7261, UINT16_MAX, 7261, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7263, UINT16_MAX, 7263, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7265, UINT16_MAX, 7265, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7267, UINT16_MAX, 7267, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7269, UINT16_MAX, 7269, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7271, UINT16_MAX, 7271, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7273, UINT16_MAX, 7273, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7275, UINT16_MAX, 7275, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7277, UINT16_MAX, 7277, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7279, UINT16_MAX, 7279, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7281, UINT16_MAX, 7281, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7283, UINT16_MAX, 7283, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7285, UINT16_MAX, 7285, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7287, UINT16_MAX, 7287, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7289, UINT16_MAX, 7289, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7291, UINT16_MAX, 7291, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7293, UINT16_MAX, 7293, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7295, UINT16_MAX, 7295, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7297, UINT16_MAX, 7297, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7299, UINT16_MAX, 7299, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7301, UINT16_MAX, 7301, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7303, UINT16_MAX, 7303, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LU, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, 7305, UINT16_MAX, 7305, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9575, UINT16_MAX, 9575, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9577, UINT16_MAX, 9577, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9579, UINT16_MAX, 9579, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9581, UINT16_MAX, 9581, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9583, UINT16_MAX, 9583, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9585, UINT16_MAX, 9585, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9587, UINT16_MAX, 9587, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9589, UINT16_MAX, 9589, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9591, UINT16_MAX, 9591, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9593, UINT16_MAX, 9593, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9595, UINT16_MAX, 9595, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9597, UINT16_MAX, 9597, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9599, UINT16_MAX, 9599, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9601, UINT16_MAX, 9601, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9603, UINT16_MAX, 9603, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9605, UINT16_MAX, 9605, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9607, UINT16_MAX, 9607, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9609, UINT16_MAX, 9609, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9611, UINT16_MAX, 9611, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9613, UINT16_MAX, 9613, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9615, UINT16_MAX, 9615, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9617, UINT16_MAX, 9617, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9619, UINT16_MAX, 9619, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9621, UINT16_MAX, 9621, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9623, UINT16_MAX, 9623, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9625, UINT16_MAX, 9625, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9627, UINT16_MAX, 9627, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9629, UINT16_MAX, 9629, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9631, UINT16_MAX, 9631, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9633, UINT16_MAX, 9633, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9635, UINT16_MAX, 9635, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9637, UINT16_MAX, 9637, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9639, UINT16_MAX, 9639, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LL, 0, UTF8PROC_BIDI_CLASS_R, 0, UINT16_MAX, UINT16_MAX, 9641, UINT16_MAX, 9641, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6603, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6585, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 7307, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 5786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 7308, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 7309, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_AL, UTF8PROC_DECOMP_TYPE_FONT, 6602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15512, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15518, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_NO, 0, UTF8PROC_BIDI_CLASS_EN, UTF8PROC_DECOMP_TYPE_COMPAT, 15522, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23716, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23719, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23722, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23725, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23728, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23731, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23734, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23737, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23740, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23743, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23746, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23749, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23752, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23755, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23758, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23761, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23764, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23767, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23770, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23773, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23776, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23779, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23782, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23788, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23791, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 2812, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 1508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 15605, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 15607, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1491, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1493, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 2812, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1494, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1495, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 2837, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1500, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1504, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 2826, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3305, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 2908, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 1511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 2925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3312, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 2834, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 13070, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15611, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15613, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 23807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15618, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 15620, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 15622, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_ON, UTF8PROC_DECOMP_TYPE_SUPER, 15624, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15626, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 1, 0, UTF8PROC_BOUNDCLASS_REGIONAL_INDICATOR}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15628, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 15630, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 4295, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7440, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7441, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7442, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7444, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3886, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7445, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7446, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7447, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 5425, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7448, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7449, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7450, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7451, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7452, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7453, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3542, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7454, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7455, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7456, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7457, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7458, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7459, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3878, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7460, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 4208, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3881, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 4209, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7461, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3598, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7462, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7463, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7464, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7465, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7466, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 4191, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 3516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7467, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7468, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7469, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_SQUARE, 7470, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23855, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23858, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23861, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23864, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23867, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23870, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23873, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23876, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_COMPAT, 23879, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 7498, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SO, 0, UTF8PROC_BIDI_CLASS_L, UTF8PROC_DECOMP_TYPE_CIRCLE, 7499, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTENDED_PICTOGRAPHIC}, + {UTF8PROC_CATEGORY_SK, 0, UTF8PROC_BIDI_CLASS_ON, 0, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_EXTEND}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7500, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7501, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7502, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7503, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7505, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7506, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7507, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7508, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7509, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7510, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7511, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7512, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7514, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7515, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7516, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7517, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7519, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7520, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7450, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7521, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7523, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7524, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7525, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7526, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7527, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3459, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7529, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7530, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7531, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7532, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7468, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7533, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7534, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7535, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7536, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7537, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7538, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7539, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7540, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7541, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7542, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7544, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7545, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7546, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7547, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7549, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7550, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7551, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7552, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7553, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7554, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7555, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7556, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7557, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7558, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7559, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7560, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7561, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7562, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7563, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7564, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7565, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7566, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7567, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7568, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7569, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7570, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7571, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7572, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7573, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7574, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7575, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7576, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7577, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7579, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7580, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7581, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7443, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7582, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7583, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7584, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7586, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7588, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7589, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7590, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7591, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7592, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7593, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7595, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7596, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7597, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7599, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7600, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7601, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7602, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7604, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7605, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7606, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3485, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7607, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7608, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7609, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7610, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7611, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7613, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7614, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7616, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7617, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7618, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7619, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7620, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7621, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7622, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7623, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7624, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7625, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7626, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7627, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7629, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7630, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7631, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7632, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7633, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3497, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7635, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7637, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7638, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7639, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7640, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7642, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7644, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7645, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7646, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7647, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7648, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7649, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7650, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7651, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7652, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7653, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7654, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7656, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7657, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7658, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7659, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7660, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7661, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7662, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7663, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7664, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7665, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7666, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7667, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7668, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7669, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7670, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7672, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7673, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7674, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7675, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7676, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7677, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7679, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7680, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7681, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7682, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7683, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7684, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7685, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7686, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7687, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7688, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7689, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7691, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7692, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7693, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7694, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7695, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7696, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7697, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7698, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7699, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7700, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7701, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7702, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7703, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7704, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7705, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7706, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7708, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7709, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7710, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7711, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7712, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7714, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7715, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7716, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7717, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7718, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7719, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7720, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7721, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7723, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7724, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7725, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7726, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7728, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7729, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7730, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7731, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7732, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7733, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7735, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7737, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7739, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7740, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7742, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7743, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7744, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7745, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7746, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7747, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7748, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7749, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7750, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7752, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7753, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7754, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7755, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7756, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7757, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7759, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7760, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7761, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7763, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7765, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7766, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7767, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7768, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7769, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7770, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7771, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7772, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7773, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7775, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7776, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7778, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7779, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7781, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7782, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7783, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7785, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7786, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7787, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7789, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7791, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7792, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7793, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7794, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7795, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7796, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7797, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7798, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7799, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7800, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7801, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7802, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7804, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7805, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7807, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7809, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7810, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7812, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7814, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7816, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7817, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7818, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7820, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7822, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7824, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7826, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7827, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7828, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7829, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7830, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7831, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7833, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7834, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7835, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7837, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7839, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7841, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7842, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7843, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7844, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7845, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7847, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7849, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7850, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7851, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7853, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7854, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7855, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7856, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7858, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7859, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7860, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7861, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7862, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7863, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7865, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7866, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7867, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7868, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7869, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7870, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7871, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7873, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7875, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7876, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7878, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7879, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7881, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7882, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7883, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7885, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7887, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7888, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7890, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7891, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7893, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7894, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7895, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7896, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7897, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7898, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7899, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7901, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7903, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7905, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7907, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7908, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7909, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7910, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7911, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7912, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7913, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7914, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7915, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7916, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7917, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7918, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7920, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7921, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7922, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7923, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7924, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7925, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7926, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7927, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7928, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7929, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7930, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7932, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7934, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7936, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7937, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7938, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7939, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7940, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7942, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7943, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7945, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7946, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7947, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7949, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7951, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7952, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7953, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7954, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7955, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7956, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7957, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7958, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7959, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7960, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7961, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7962, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7963, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7964, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7965, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7966, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3587, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7967, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7969, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7970, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7971, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7972, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7973, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7974, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7976, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7978, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7979, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7980, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3594, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7981, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7983, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7984, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7985, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7986, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7987, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7989, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7991, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7992, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7993, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7994, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7996, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7997, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 7999, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8001, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8002, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8003, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8004, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8006, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8007, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8008, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8009, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8010, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8011, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8012, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8013, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8015, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8016, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8017, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8018, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8020, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8021, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8022, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8023, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8024, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8026, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8028, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8029, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8030, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8031, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8033, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8034, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8036, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8037, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8039, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8040, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8041, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8042, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8043, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8044, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8045, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8046, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8048, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8049, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8050, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8051, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8052, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8053, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8055, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8056, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8058, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8060, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3642, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8062, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3646, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8063, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8064, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8065, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8066, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 3651, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, + {UTF8PROC_CATEGORY_LO, 0, UTF8PROC_BIDI_CLASS_L, 0, 8067, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX, false, false, false, false, 2, 0, UTF8PROC_BOUNDCLASS_OTHER}, +}; + +static const utf8proc_uint16_t utf8proc_combinations[] = { + 0, 46, 192, 193, 194, 195, 196, 197, 0, + 256, 258, 260, 550, 461, 0, 0, 512, + 514, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 7680, 7840, 0, 0, 0, 0, 0, 7842, +1, 11, + 262, 264, 0, 0, 0, 199, 0, 0, + 0, 266, 268, +0, 46, 200, 201, 202, 7868, 203, + 0, 552, 274, 276, 280, 278, 282, 0, + 0, 516, 518, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7864, 0, 7704, 7706, 0, + 0, 7866, +0, 46, 204, 205, 206, 296, 207, 0, + 0, 298, 300, 302, 304, 463, 0, 0, + 520, 522, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7882, 0, 0, 7724, 0, 0, + 7880, +0, 42, 504, 323, 0, 209, 0, 0, 325, + 0, 0, 0, 7748, 327, 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, 7750, 7752, 7754, +0, 46, 210, 211, 212, 213, + 214, 0, 0, 332, 334, 490, 558, 465, + 336, 416, 524, 526, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7884, 0, 0, 0, + 0, 0, 7886, +0, 46, 217, 218, 219, 360, 220, + 366, 0, 362, 364, 370, 0, 467, 368, + 431, 532, 534, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7908, 0, 7798, 7796, 0, + 7794, 7910, +0, 46, 7922, 221, 374, 7928, 376, 0, + 0, 562, 0, 0, 7822, 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, 7924, 0, 0, 0, 0, 0, + 7926, +0, 46, 224, 225, 226, 227, 228, 229, 0, + 257, 259, 261, 551, 462, 0, 0, 513, + 515, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 7681, 7841, 0, 0, 0, 0, 0, 7843, +1, 11, + 263, 265, 0, 0, 0, 231, 0, 0, + 0, 267, 269, +0, 46, 232, 233, 234, 7869, 235, + 0, 553, 275, 277, 281, 279, 283, 0, + 0, 517, 519, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7865, 0, 7705, 7707, 0, + 0, 7867, +0, 46, 236, 237, 238, 297, 239, 0, + 0, 299, 301, 303, 0, 464, 0, 0, + 521, 523, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7883, 0, 0, 7725, 0, 0, + 7881, +0, 42, 505, 324, 0, 241, 0, 0, 326, + 0, 0, 0, 7749, 328, 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, 7751, 7753, 7755, +0, 46, 242, 243, 244, 245, + 246, 0, 0, 333, 335, 491, 559, 466, + 337, 417, 525, 527, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7885, 0, 0, 0, + 0, 0, 7887, +0, 46, 249, 250, 251, 361, 252, + 367, 0, 363, 365, 371, 0, 468, 369, + 432, 533, 535, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7909, 0, 7799, 7797, 0, + 7795, 7911, +0, 46, 7923, 253, 375, 7929, 255, 7833, + 0, 563, 0, 0, 7823, 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, 7925, 0, 0, 0, 0, 0, + 7927, +6, 42, 7696, 0, 0, 0, 7690, 270, 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, 7692, 7694, 7698, +6, 42, 7697, 0, + 0, 0, 7691, 271, 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, + 7693, 7695, 7699, +1, 11, 500, 284, 0, 0, 0, + 290, 7712, 286, 0, 288, 486, +1, 11, 501, 285, + 0, 0, 0, 291, 7713, 287, 0, 289, + 487, +2, 44, 292, 0, 7718, 0, 7720, 0, 0, + 0, 7714, 542, 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, 7716, + 0, 0, 0, 7722, +2, 44, 293, 0, 7719, 0, + 7721, 0, 0, 0, 7715, 543, 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, 7717, 7830, 0, 0, 7723, +2, 2, 308, +2, 11, + 309, 0, 0, 0, 0, 0, 0, 0, + 0, 496, +1, 41, 7728, 0, 0, 0, 0, 310, + 0, 0, 0, 0, 488, 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, 7730, 7732, +1, 41, 7729, 0, 0, 0, 0, + 311, 0, 0, 0, 0, 489, 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, 7731, 7733, +1, 42, 313, 0, 0, 0, + 0, 315, 0, 0, 0, 0, 317, 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, 7734, 7738, 7740, +1, 42, 314, 0, + 0, 0, 0, 316, 0, 0, 0, 0, + 318, 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, 7735, 7739, 7741, +1, 41, + 340, 0, 0, 0, 0, 342, 0, 0, + 0, 7768, 344, 0, 0, 528, 530, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7770, + 7774, +1, 41, 341, 0, 0, 0, 0, 343, 0, + 0, 0, 7769, 345, 0, 0, 529, 531, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 7771, 7775, +1, 40, 346, 348, 0, 0, 0, 350, + 0, 0, 0, 7776, 352, 0, 0, 0, + 0, 536, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7778, +1, 40, 347, 349, 0, 0, 0, 351, + 0, 0, 0, 7777, 353, 0, 0, 0, + 0, 537, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7779, +6, 42, 354, 0, 0, 0, 7786, 356, + 0, 0, 0, 0, 538, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7788, 7790, 7792, +4, 42, 7831, + 0, 355, 0, 0, 0, 7787, 357, 0, + 0, 0, 0, 539, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7789, 7791, 7793, +0, 40, 7808, 7810, + 372, 0, 7812, 0, 0, 0, 0, 0, + 7814, 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, 7816, +0, 40, 7809, + 7811, 373, 0, 7813, 7832, 0, 0, 0, + 0, 7815, 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, 7817, +1, 41, + 377, 7824, 0, 0, 0, 0, 0, 0, + 0, 379, 381, 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, 7826, + 7828, +1, 41, 378, 7825, 0, 0, 0, 0, 0, + 0, 0, 380, 382, 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, + 7827, 7829, +0, 11, 475, 471, 0, 0, 0, 0, + 0, 469, 0, 0, 0, 473, +0, 11, 476, 472, + 0, 0, 0, 0, 0, 470, 0, 0, + 0, 474, +7, 7, 478, +7, 7, 479, +7, 7, 480, +7, 7, 481, +1, 7, 508, 0, + 0, 0, 0, 0, 482, +1, 7, 509, 0, 0, + 0, 0, 0, 483, +7, 7, 492, +7, 7, 493, +11, 11, 494, +11, 11, 495, +1, 1, + 506, +1, 1, 507, +1, 1, 510, +1, 1, 511, +7, 7, 554, +7, 7, 555, +1, 7, 7756, 0, + 0, 7758, 0, 0, 556, +1, 7, 7757, 0, 0, + 7759, 0, 0, 557, +7, 7, 560, +7, 7, 561, +0, 49, 8173, 901, + 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, 8129, +0, 50, + 8122, 902, 0, 0, 0, 0, 0, 8121, + 8120, 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, 7944, + 7945, 0, 8124, +0, 48, 8136, 904, 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, 7960, 7961, +0, 50, 8138, 905, 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, 7976, 7977, 0, 8140, +0, 48, 8154, + 906, 0, 0, 938, 0, 0, 8153, 8152, + 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, 7992, 7993, +0, 48, + 8184, 908, 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, 8008, + 8009, +0, 48, 8170, 910, 0, 0, 939, 0, 0, + 8169, 8168, 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, 8025, +0, 50, 8186, 911, 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, 8040, 8041, 0, 8188, +0, 49, 8146, 912, 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, 8151, +0, 50, 8048, + 940, 0, 0, 0, 0, 0, 8113, 8112, + 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, 7936, 7937, + 8118, 8115, +0, 48, 8050, 941, 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, 7952, 7953, +0, 50, 8052, 942, 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, 7968, 7969, 8134, 8131, +0, 49, 8054, 943, + 0, 0, 970, 0, 0, 8145, 8144, 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, 7984, 7985, 8150, +0, 49, + 8162, 944, 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, 8167, +0, 49, 8058, 973, 0, 0, 971, 0, + 0, 8161, 8160, 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, 8016, 8017, 8166, +0, 48, 8056, 972, 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, 8000, 8001, +0, 50, 8060, 974, 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, 8032, 8033, 8182, 8179, +1, 4, + 979, 0, 0, 980, +0, 8, 1024, 0, 0, 0, + 1025, 0, 0, 0, 1238, +1, 1, 1027, +4, 4, 1031, +1, 1, 1036, +0, 8, + 1037, 0, 0, 0, 1252, 0, 0, 1250, + 1049, +4, 12, 1264, 0, 0, 1262, 1038, 0, 0, + 0, 1266, +0, 8, 1117, 0, 0, 0, 1253, 0, + 0, 1251, 1081, +0, 8, 1104, 0, 0, 0, 1105, + 0, 0, 0, 1239, +1, 1, 1107, +4, 4, 1111, +1, 1, 1116, +4, 12, 1265, + 0, 0, 1263, 1118, 0, 0, 0, 1267, +14, 14, + 1142, +14, 14, 1143, +4, 8, 1244, 0, 0, 0, 1217, +4, 8, 1245, + 0, 0, 0, 1218, +4, 8, 1234, 0, 0, 0, + 1232, +4, 8, 1235, 0, 0, 0, 1233, +4, 4, 1242, +4, 4, 1243, +4, 4, + 1246, +4, 4, 1247, +4, 4, 1254, +4, 4, 1255, +4, 4, 1258, +4, 4, 1259, +4, 4, 1260, +4, 4, 1261, +4, 4, + 1268, +4, 4, 1269, +4, 4, 1272, +4, 4, 1273, +17, 19, 1570, 1571, 1573, +18, 18, 1572, +18, 18, + 1574, +18, 18, 1728, +18, 18, 1730, +18, 18, 1747, +20, 20, 2345, +20, 20, 2353, +20, 20, 2356, +21, 22, 2507, + 2508, +23, 25, 2888, 2891, 2892, +26, 26, 2964, +26, 27, 3020, 3018, +27, 27, 3019, +28, 28, + 3144, +29, 29, 3264, +29, 31, 3271, 3272, 3274, +29, 29, 3275, +32, 33, 3402, 3404, +32, 32, + 3403, +34, 36, 3546, 3548, 3550, +34, 34, 3549, +37, 37, 4134, +38, 38, 6918, +38, 38, 6920, +38, 38, + 6922, +38, 38, 6924, +38, 38, 6926, +38, 38, 6930, +38, 38, 6971, +38, 38, 6973, +38, 38, 6976, +38, 38, 6977, +38, 38, + 6979, +10, 41, 7682, 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, 7684, + 7686, +10, 41, 7683, 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, 7685, + 7687, +1, 1, 7688, +1, 1, 7689, +0, 1, 7700, 7702, +0, 1, 7701, 7703, +8, 8, 7708, +8, 8, + 7709, +10, 10, 7710, +10, 10, 7711, +1, 1, 7726, +1, 1, 7727, +7, 7, 7736, +7, 7, 7737, +1, 40, 7742, + 0, 0, 0, 0, 0, 0, 0, 0, + 7744, 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, 7746, +1, 40, 7743, + 0, 0, 0, 0, 0, 0, 0, 0, + 7745, 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, 7747, +0, 1, 7760, + 7762, +0, 1, 7761, 7763, +1, 10, 7764, 0, 0, 0, 0, + 0, 0, 0, 0, 7766, +1, 10, 7765, 0, 0, + 0, 0, 0, 0, 0, 0, 7767, +7, 7, 7772, +7, 7, + 7773, +10, 10, 7780, +10, 10, 7781, +10, 10, 7782, +10, 10, 7783, +10, 10, 7784, +10, 10, 7785, +1, 1, 7800, +1, 1, + 7801, +4, 4, 7802, +4, 4, 7803, +3, 40, 7804, 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, + 7806, +3, 40, 7805, 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, 7807, +4, 10, 7820, + 0, 0, 0, 0, 0, 7818, +4, 10, 7821, 0, + 0, 0, 0, 0, 7819, +10, 10, 7835, +0, 46, 7846, 7844, + 0, 7850, 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, 7848, +0, 46, 7847, 7845, 0, + 7851, 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, 7849, +2, 8, 7852, 0, 0, 0, + 0, 0, 7862, +2, 8, 7853, 0, 0, 0, 0, + 0, 7863, +0, 46, 7856, 7854, 0, 7860, 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, + 7858, +0, 46, 7857, 7855, 0, 7861, 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, 7859, +0, 46, + 7872, 7870, 0, 7876, 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, 7874, +0, 46, 7873, + 7871, 0, 7877, 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, 7875, +2, 2, 7878, +2, 2, 7879, +0, 46, + 7890, 7888, 0, 7894, 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, 7892, +0, 46, 7891, + 7889, 0, 7895, 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, 7893, +2, 2, 7896, +2, 2, 7897, +0, 46, + 7900, 7898, 0, 7904, 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, + 7906, 0, 0, 0, 0, 0, 7902, +0, 46, 7901, + 7899, 0, 7905, 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, 7907, + 0, 0, 0, 0, 0, 7903, +0, 46, 7914, 7912, + 0, 7918, 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, 7920, 0, + 0, 0, 0, 0, 7916, +0, 46, 7915, 7913, 0, + 7919, 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, 7921, 0, 0, + 0, 0, 0, 7917, +0, 50, 7938, 7940, 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, 7942, 8064, +0, 50, 7939, + 7941, 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, + 7943, 8065, +0, 50, 7946, 7948, 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, 7950, 8072, +0, 50, 7947, 7949, 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, 7951, 8073, +0, 1, + 7954, 7956, +0, 1, 7955, 7957, +0, 1, 7962, 7964, +0, 1, 7963, 7965, +0, 50, + 7970, 7972, 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, 7974, 8080, +0, 50, 7971, 7973, 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, 7975, 8081, +0, 50, 7978, 7980, + 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, 7982, + 8088, +0, 50, 7979, 7981, 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, 7983, 8089, +0, 49, 7986, 7988, 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, 7990, +0, 49, 7987, 7989, + 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, 7991, +0, 49, + 7994, 7996, 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, 7998, +0, 49, 7995, 7997, 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, 7999, +0, 1, 8002, 8004, +0, 1, 8003, 8005, +0, 1, + 8010, 8012, +0, 1, 8011, 8013, +0, 49, 8018, 8020, 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, 8022, +0, 49, 8019, 8021, + 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, 8023, +0, 49, + 8027, 8029, 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, 8031, +0, 50, 8034, 8036, 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, 8038, 8096, +0, 50, 8035, 8037, 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, 8039, 8097, +0, 50, + 8042, 8044, 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, 8046, 8104, +0, 50, 8043, 8045, 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, 8047, 8105, +50, 50, 8066, +50, 50, 8067, +50, 50, + 8068, +50, 50, 8069, +50, 50, 8070, +50, 50, 8071, +50, 50, 8074, +50, 50, 8075, +50, 50, 8076, +50, 50, 8077, +50, 50, + 8078, +50, 50, 8079, +50, 50, 8082, +50, 50, 8083, +50, 50, 8084, +50, 50, 8085, +50, 50, 8086, +50, 50, 8087, +50, 50, + 8090, +50, 50, 8091, +50, 50, 8092, +50, 50, 8093, +50, 50, 8094, +50, 50, 8095, +50, 50, 8098, +50, 50, 8099, +50, 50, + 8100, +50, 50, 8101, +50, 50, 8102, +50, 50, 8103, +50, 50, 8106, +50, 50, 8107, +50, 50, 8108, +50, 50, 8109, +50, 50, + 8110, +50, 50, 8111, +50, 50, 8114, +50, 50, 8116, +50, 50, 8119, +50, 50, 8130, +50, 50, 8132, +50, 50, 8135, +0, 49, + 8141, 8142, 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, 8143, +0, 49, 8157, 8158, 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, 8159, +47, 48, 8164, 8165, +48, 48, 8172, +50, 50, 8178, +50, 50, + 8180, +50, 50, 8183, +51, 51, 8602, +51, 51, 8603, +51, 51, 8622, +51, 51, 8653, +51, 51, 8654, +51, 51, 8655, +51, 51, + 8708, +51, 51, 8713, +51, 51, 8716, +51, 51, 8740, +51, 51, 8742, +51, 51, 8769, +51, 51, 8772, +51, 51, 8775, +51, 51, + 8777, +51, 51, 8800, +51, 51, 8802, +51, 51, 8813, +51, 51, 8814, +51, 51, 8815, +51, 51, 8816, +51, 51, 8817, +51, 51, + 8820, +51, 51, 8821, +51, 51, 8824, +51, 51, 8825, +51, 51, 8832, +51, 51, 8833, +51, 51, 8836, +51, 51, 8837, +51, 51, + 8840, +51, 51, 8841, +51, 51, 8876, +51, 51, 8877, +51, 51, 8878, +51, 51, 8879, +51, 51, 8928, +51, 51, 8929, +51, 51, + 8930, +51, 51, 8931, +51, 51, 8938, +51, 51, 8939, +51, 51, 8940, +51, 51, 8941, +51, 51, 10972, +52, 52, 12364, +52, 52, + 12366, +52, 52, 12368, +52, 52, 12370, +52, 52, 12372, +52, 52, 12374, +52, 52, 12376, +52, 52, 12378, +52, 52, 12380, +52, 52, + 12382, +52, 52, 12384, +52, 52, 12386, +52, 52, 12389, +52, 52, 12391, +52, 52, 12393, +52, 53, 12400, 12401, +52, 53, + 12403, 12404, +52, 53, 12406, 12407, +52, 53, 12409, 12410, +52, 53, 12412, 12413, +52, 52, + 12436, +52, 52, 12446, +52, 52, 12460, +52, 52, 12462, +52, 52, 12464, +52, 52, 12466, +52, 52, 12468, +52, 52, 12470, +52, 52, + 12472, +52, 52, 12474, +52, 52, 12476, +52, 52, 12478, +52, 52, 12480, +52, 52, 12482, +52, 52, 12485, +52, 52, 12487, +52, 52, + 12489, +52, 53, 12496, 12497, +52, 53, 12499, 12500, +52, 53, 12502, 12503, +52, 53, 12505, + 12506, +52, 53, 12508, 12509, +52, 52, 12532, +52, 52, 12535, +52, 52, 12536, +52, 52, 12537, +52, 52, 12538, +52, 52, + 12542, +54, 55, 1, 4250, +54, 55, 1, 4252, +54, 55, 1, 4267, +56, 57, 1, 4398, +56, 57, 1, 4399, +58, 61, 1, 4939, 1, 4940, +62, 67, + 1, 5307, 1, 5308, 1, 5310, +68, 69, 1, 5562, +68, 69, 1, 5563, +70, 71, 1, 53598, +70, 71, 1, 53599, +72, 81, 1, 53600, + 1, 53601, 1, 53602, 1, 53603, 1, 53604, +70, 71, 1, 53691, +70, 71, 1, 53692, +72, 75, 1, 53693, 1, 53695, +72, 75, + 1, 53694, 1, 53696, +}; + diff --git a/hilti/src/asan.cc b/hilti/src/asan.cc new file mode 100644 index 000000000..54a259eb1 --- /dev/null +++ b/hilti/src/asan.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// We link this into a tiny static library so that it overrides the +// corresponding weak function in the ASAN runtime. (That doesn't work if +// it's just part of the standard shared HILTI library, at least not on +// Linux.) +// +// The code itself is borrowed/adapted from Chromium. + +#include + +extern "C" __attribute__((used)) __attribute__((visibility("default"))) const char* __asan_default_options() { + // detect_container_overflow=0: Avoid false positives in LLVM, presumably + // compiled when without ASAN support. + // + // detect_odr_violation=0: Getting erros for __asan_register_globals + // otherwise. + // + // detect_leaks=1: Enable, doesn't always(?) seem to be on by default. + return "detect_container_overflow=0:detect_odr_violation=0:detect_leaks=1"; +} + +// Our CMake confiog explicitly tells the linker that this function is +// undefined, which will then lead to this whole file being included into the +// linking process. +extern "C" void _sanitizer_options_link_helper() {} diff --git a/hilti/src/ast/builder/builder.cc b/hilti/src/ast/builder/builder.cc new file mode 100644 index 000000000..206323803 --- /dev/null +++ b/hilti/src/ast/builder/builder.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include + +using namespace hilti; +using namespace hilti::builder; + +using util::fmt; + +Expression Builder::addTmp(const std::string& prefix, const Type& t) { + int n = 0; + + if ( auto i = _tmps.find(prefix); i != _tmps.end() ) + n = i->second; + + ID tmp; + + if ( ++n == 1 ) + tmp = ID(fmt("__%s", prefix)); + else + tmp = ID(fmt("__%s_%d", prefix, n)); + + _tmps[prefix] = n; + _block._add(builder::local(tmp, t)); + return builder::id(tmp); +} + +Expression Builder::addTmp(const std::string& prefix, const Expression& init) { + int n = 0; + + if ( auto i = _tmps.find(prefix); i != _tmps.end() ) + n = i->second; + + ID tmp; + + if ( ++n == 1 ) + tmp = ID(fmt("__%s", prefix)); + else + tmp = ID(fmt("__%s_%d", prefix, n)); + + _tmps[prefix] = n; + _block._add(builder::local(tmp, init)); + return builder::id(tmp); +} + +Expression Builder::addTmp(const std::string& prefix, const Type& t, const Expression& init) { + int n = 0; + + if ( auto i = _tmps.find(prefix); i != _tmps.end() ) + n = i->second; + + ID tmp; + + if ( ++n == 1 ) + tmp = ID(fmt("__%s", prefix)); + else + tmp = ID(fmt("__%s_%d", prefix, n)); + + _tmps[prefix] = n; + _block._add(builder::local(tmp, t, init)); + return builder::id(tmp); +} + +void Builder::addDebugMsg(const std::string& stream, const std::string& fmt, std::vector args) { + if ( ! _context->options().debug ) + return; + + Expression call; + + if ( args.empty() ) + call = builder::call("hilti::debug", {builder::string(stream), builder::string(fmt)}); + else if ( args.size() == 1 ) { + auto msg = builder::modulo(builder::string(fmt), std::move(args.front())); + call = builder::call("hilti::debug", {builder::string(stream), std::move(msg)}); + } + else { + auto msg = builder::modulo(builder::string(fmt), builder::tuple(args)); + call = builder::call("hilti::debug", {builder::string(stream), std::move(msg)}); + } + + _block._add(statement::Expression(call, call.meta())); +} + +void Builder::addDebugIndent(const std::string& stream) { + if ( ! _context->options().debug ) + return; + + auto call = builder::call("hilti::debugIndent", {builder::string(stream)}); + _block._add(statement::Expression(std::move(call))); +} + +void Builder::addDebugDedent(const std::string& stream) { + if ( ! _context->options().debug ) + return; + + auto call = builder::call("hilti::debugDedent", {builder::string(stream)}); + _block._add(statement::Expression(std::move(call))); +} diff --git a/hilti/src/ast/expression.cc b/hilti/src/ast/expression.cc new file mode 100644 index 000000000..6d0de2db6 --- /dev/null +++ b/hilti/src/ast/expression.cc @@ -0,0 +1,3 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include diff --git a/hilti/src/ast/expressions/id.cc b/hilti/src/ast/expressions/id.cc new file mode 100644 index 000000000..d1d915863 --- /dev/null +++ b/hilti/src/ast/expressions/id.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti; + +Type expression::ResolvedID::type() const { + struct Visitor : hilti::visitor::PreOrder { + result_t operator()(const declaration::Constant& c) { return c.value().type(); } + result_t operator()(const declaration::Expression& e) { return e.expression().type(); } + result_t operator()(const declaration::Forward& f) { return *dispatch(f.callback()()); } + result_t operator()(const declaration::Function& f) { return f.function().type(); } + result_t operator()(const declaration::GlobalVariable& v) { return v.type(); } + result_t operator()(const declaration::LocalVariable& v) { return v.type(); } + result_t operator()(const declaration::Parameter& p) { return p.type(); } + result_t operator()(const declaration::Type& t) { return type::Type_(t.type(), t.meta()); } + }; + + if ( ! isValid() ) + return type::unknown; + + if ( auto x = Visitor().dispatch(Node(declaration())) ) + return *x; + + logger().internalError(util::fmt("unsupported declaration type %s", declaration().typename_()), *this); +} + +bool expression::ResolvedID::isConstant() const { + struct Visitor : hilti::visitor::PreOrder { + result_t operator()(const declaration::Constant& c) { return true; } // NOLINT + result_t operator()(const declaration::Expression& e) { return e.expression().isConstant(); } + result_t operator()(const declaration::Forward& f) { return *dispatch(f.callback()()); } + result_t operator()(const declaration::Function& f) { return true; } // NOLINT + result_t operator()(const declaration::GlobalVariable& v) { return v.isConstant(); } + result_t operator()(const declaration::LocalVariable& v) { return v.isConstant(); } + result_t operator()(const declaration::Parameter& p) { return p.isConstant(); } + }; + + if ( auto x = Visitor().dispatch(declaration()) ) + return *x; + + logger().internalError(util::fmt("unsupported declaration type %s", declaration().typename_()), *this); +} diff --git a/hilti/src/ast/location.cc b/hilti/src/ast/location.cc new file mode 100644 index 000000000..e457fa3fc --- /dev/null +++ b/hilti/src/ast/location.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti; + +const Location location::None; + +Location::operator bool() const { return _file != location::None._file; } + +std::string Location::render(bool no_path) const { + std::string lines; + + if ( _from >= 0 ) { + lines = util::fmt(":%d", _from); + + if ( _to >= 0 && _to != _from ) { + lines += util::fmt("-%d", _to); + } + } + + auto path = no_path ? _file.filename() : _file; + return util::fmt("%s%s", path.generic_string(), lines); +} diff --git a/hilti/src/ast/module.cc b/hilti/src/ast/module.cc new file mode 100644 index 000000000..a9aab33ed --- /dev/null +++ b/hilti/src/ast/module.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti; + +NodeRef Module::preserve(Node n) { + detail::clearErrors(&n); + _preserved.push_back(std::move(n)); + return NodeRef(_preserved.back()); +} + +Result Module::moduleProperty(const ID& id) const { + for ( const auto& d : declarations() ) { + if ( auto p = d.tryAs(); p && p->id() == id ) + return *p; + } + + return result::Error("no property of specified id"); +} + +std::vector Module::moduleProperties(const ID& id) const { + std::vector props; + + for ( const auto& d : declarations() ) { + if ( auto p = d.tryAs(); p && p->id() == id ) + props.push_back(*p); + } + + return props; +} diff --git a/hilti/src/ast/node.cc b/hilti/src/ast/node.cc new file mode 100644 index 000000000..f98cf808f --- /dev/null +++ b/hilti/src/ast/node.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti; + +std::string Node::render(bool include_location) const { + auto f = [&](const node::Properties::value_type& x) { + return util::fmt("%s=%s", x.first, node::detail::to_string(x.second)); + }; + + std::vector props; + + for ( const auto& x : properties() ) + props.push_back(f(x)); + + std::string sprops; + + if ( ! props.empty() ) + sprops = util::fmt(" <%s>", util::join(props, " ")); + + // Prettify the name a bit. + auto name = typename_(); + name = util::replace(name, "hilti::", ""); + + if ( util::startsWith(name, "detail::") ) + name = util::join(util::slice(util::split(name, "::"), 2), "::"); + + auto location = (include_location && meta().location()) ? util::fmt(" (%s)", meta().location().render(true)) : ""; + auto id = rid() ? util::fmt(" %s", renderedRid()) : ""; + auto orig = originalNode() ? util::fmt(" (original %s)", originalNode()->renderedRid()) : ""; + auto err = error() ? util::fmt(" [ERROR: %s]", *error()) : ""; + + std::string type; + + if ( auto x = this->tryAs() ) + type = util::fmt(" (type: %s)", x->type()); + + else if ( auto x = this->tryAs() ) + type = util::fmt(" (type: %s)", x->type()); + + auto s = util::fmt("%s%s%s%s%s%s%s", name, id, orig, sprops, type, location, err); + + if ( auto t = this->tryAs() ) { + std::vector flags; + + if ( type::isConstant(*t) ) + flags.emplace_back("const"); + else + flags.emplace_back("non-const"); + + s += util::fmt(" (%s)", util::join(flags, ", ")); + + if ( t->hasFlag(type::Flag::NoInheritScope) ) + s += util::fmt(" (top-level scope)"); + + if ( auto tid = t->typeID() ) + s += util::fmt(" (type-id: %s)", *tid); + + if ( auto cppid = t->cxxID() ) + s += util::fmt(" (cxx-id: %s)", *cppid); + } + + return s; +} + +void Node::print(std::ostream& out, bool compact) const { detail::printAST(*this, out, compact); } + +node::Properties operator+(const node::Properties& p1, const node::Properties& p2) { + node::Properties p; + + for ( auto& i : p1 ) + p.insert(i); + + for ( auto& i : p2 ) + p.insert(i); + + return p; +} diff --git a/hilti/src/ast/node_ref.cc b/hilti/src/ast/node_ref.cc new file mode 100644 index 000000000..d30f63d40 --- /dev/null +++ b/hilti/src/ast/node_ref.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti; + +uint64_t node_ref::detail::Control::_rid_counter = 0; + +NodeRef::NodeRef(Node& n) : _control(n._control()) {} + +Node* NodeRef::_node() const { + if ( ! _control ) + throw node_ref::Invalid("accesd to uninitalized node reference"); + + if ( ! _control->_node ) + throw node_ref::Invalid("dangling node reference"); + + return _control->_node; +} diff --git a/hilti/src/ast/scope.cc b/hilti/src/ast/scope.cc new file mode 100644 index 000000000..2daeceaf0 --- /dev/null +++ b/hilti/src/ast/scope.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include + +using namespace hilti; + +void Scope::insert(const ID& id, NodeRef n) { + auto& nodes = _items[std::string(id)]; + nodes.push_back(std::move(n)); +} + +void Scope::insert(const ID& id, Node&& n) { + auto p = std::make_shared(std::move(n)); + _nodes.push_back(p); + insert(id, NodeRef(*_nodes.back())); +} + +static auto createRefs(const std::vector& refs, const std::string& ns, bool external) { + std::vector result; + + result.reserve(refs.size()); + for ( auto r : refs ) { + result.push_back(Scope::Referee{.node = std::move(r.node), + .qualified = (ns + "::" + r.qualified), + .external = (external || r.external)}); + } + + return result; +} + +static auto createRefs(std::vector refs, const std::string& id, bool external) { + std::vector result; + + result.reserve(refs.size()); + for ( auto& n : refs ) { + result.push_back(Scope::Referee{.node = std::move(n), .qualified = id, .external = external}); + } + + return result; +} + + +std::vector Scope::_findID(const Scope* scope, const ID& id, bool external) const { + // Try all subpaths. + // + // TOOD: This method needs a cleanup, pretty ugly. + std::string h = id; + std::string t = "$ $"; // non-empty, illegal-ID dummy + + std::vector result; + + while ( true ) { + if ( t.empty() ) + return {}; + + if ( auto i = scope->_items.find(h); i != scope->_items.end() ) { + if ( t == "$ $" ) + return createRefs(i->second, h, external); + + for ( const auto& v : (*i).second ) { + auto e = v->isA(); + + if ( auto x = _findID((*v).scope().get(), ID(t), external || e); ! x.empty() ) + return createRefs(x, h, external); + } + + return {}; + } + + std::string nt; + std::tie(h, nt) = util::rsplit1(h, "::"); + t = (! t.empty() && ! nt.empty() && t != "$ $" ? util::fmt("%s::%s", nt, t) : nt); + } +} + +std::vector Scope::_findID(const ID& id, bool external) const { return _findID(this, id, external); } + +void Scope::render(std::ostream& out, const std::string& prefix) const { + for ( const auto& [k, v] : items() ) { + for ( const auto& x : v ) { + auto s = util::fmt("%s%s -> %s", prefix, k, x ? x->render(false) : ""); + + if ( auto d = x->tryAs() ) + s += util::fmt(" (type: %s)", d->expression().type()); + + out << s << '\n'; + } + } +} diff --git a/hilti/src/ast/type.cc b/hilti/src/ast/type.cc new file mode 100644 index 000000000..af86288ac --- /dev/null +++ b/hilti/src/ast/type.cc @@ -0,0 +1,6 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti; diff --git a/hilti/src/ast/types/enum.cc b/hilti/src/ast/types/enum.cc new file mode 100644 index 000000000..c3125dee9 --- /dev/null +++ b/hilti/src/ast/types/enum.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include + +using namespace hilti; + +std::vector type::Enum::_normalizeLabels(std::vector labels) { + auto max = std::max_element(labels.begin(), labels.end(), [](auto l1, auto l2) { return l1.value() < l2.value(); }); + auto next_value = (max != labels.end() ? max->value() + 1 : 0); + + std::vector nlabels; + + for ( auto l : labels ) { + if ( util::tolower(l.id()) == "undef" ) + throw std::out_of_range("reserved enum label 'Undef' cannot be redefined"); + + if ( l.value() < 0 ) + nlabels.emplace_back(l.id(), next_value++, l.meta()); + else + nlabels.push_back(std::move(l)); + } + + nlabels.emplace_back(ID("Undef"), -1); + + return nlabels; +} diff --git a/hilti/src/ast/types/integer.cc b/hilti/src/ast/types/integer.cc new file mode 100644 index 000000000..f4a8d7854 --- /dev/null +++ b/hilti/src/ast/types/integer.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include + +using namespace hilti; +using namespace hilti::type; + +std::vector SignedInteger::typeParameters() const { + return {Ctor(ctor::SignedInteger(static_cast(width()), 64))}; +} + +std::vector UnsignedInteger::typeParameters() const { return {Ctor(ctor::UnsignedInteger(width(), 64))}; } diff --git a/hilti/src/ast/types/tuple.cc b/hilti/src/ast/types/tuple.cc new file mode 100644 index 000000000..c34a25e6d --- /dev/null +++ b/hilti/src/ast/types/tuple.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include + +using namespace hilti; + +std::vector type::Tuple::ids() const { + auto ids = childsOfType(); + if ( ! ids.empty() ) + return ids; + + return std::vector(types().size(), ID()); +} + +std::optional> type::Tuple::elementByID(const ID& id) { + for ( const auto& [i, e] : util::enumerate(elements()) ) { + if ( e.first == id ) + return std::make_pair(i, e.second); + } + + return {}; +} diff --git a/hilti/src/base/code-formatter.cc b/hilti/src/base/code-formatter.cc new file mode 100644 index 000000000..4a21fcefd --- /dev/null +++ b/hilti/src/base/code-formatter.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti; +using namespace hilti::code_formatter; + +void CodeFormatter::next() { + // _out << "| " << _at_bol << "/" << _indent << " "; + if ( _at_bol ) { + _out << std::string(_indent * 4, ' '); + _at_bol = false; + } +} + +void CodeFormatter::separator() { + if ( _did_sep ) + return; + + _out << '\n'; + _at_bol = true; + _did_sep = true; + _in_comment = false; +} + +void CodeFormatter::eol() { + _out << '\n'; + _did_sep = false; + _at_bol = true; + _in_comment = false; +} + +void CodeFormatter::eos() { + next(); + _out << ';'; + eol(); +} + +void CodeFormatter::quoted(const std::string& s) { + next(); + _out << '"' << util::escapeUTF8(s) << '"'; +} + +void CodeFormatter::comment(const std::string& s) { + if ( ! _in_comment ) + separator(); + + next(); + _out << _comment << ' ' << s; + eol(); + _in_comment = true; +} + +CodeFormatter& CodeFormatter::printString(const std::string& s) { + std::string::size_type i = 0; + + while ( i < s.size() ) { + auto j = s.find('\n', i); + + if ( j == std::string::npos ) + break; + + if ( j != i ) { + next(); + _out << s.substr(i, j - i); + } + + eol(); + i = j + 1; + } + + if ( i != std::string::npos ) { + next(); + _out << s.substr(i); + } + + return *this; +} diff --git a/hilti/src/base/logger.cc b/hilti/src/base/logger.cc new file mode 100644 index 000000000..ce7d617c7 --- /dev/null +++ b/hilti/src/base/logger.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +using namespace hilti; + +std::unique_ptr Logger::_singleton; + +logging::DebugStream::DebugStream(const std::string& name) : _name(name) { + if ( auto i = _all.find(name); i != _all.end() ) + _id = i->second._id; + else { + _id = _all.size(); + _all.emplace(name, *this); + } +} + +std::vector logging::DebugStream::all() { + std::vector keys; + + keys.reserve(_all.size()); + for ( const auto& s : _all ) + keys.push_back(s.first); + + return keys; +} + +logging::Stream::Buffer::Buffer(logging::Level level) : std::stringbuf(std::ios_base::out), _level(level) { + setp(nullptr, nullptr); // make every character go through overflow() +} + +logging::Stream::Buffer::Buffer(logging::DebugStream dbg) + : std::stringbuf(std::ios_base::out), _level(Level::Debug), _dbg(dbg) { + setp(nullptr, nullptr); // make every character go through overflow() +} + +int logging::Stream::Buffer::sync() { + if ( _buffer.empty() ) + return 0; + + if ( _dbg ) + logger()._debug(*_dbg, util::rtrim(_buffer)); + else + logger().log(_level, util::rtrim(_buffer)); + + _buffer.clear(); + return 0; +} + +int logging::Stream::Buffer::overflow(int ch) { + if ( ch != traits_type::eof() ) { + _buffer.push_back(ch); + + if ( ch == '\n' ) + sync(); + } + + return ch; +} + +std::unique_ptr hilti::setLogger(std::unique_ptr logger) { + std::swap(Logger::_singleton, logger); + return logger; +} + +void Logger::debugEnable(const logging::DebugStream& dbg) { + if ( _debug_streams.find(dbg) == _debug_streams.end() ) + _debug_streams[dbg] = 0; +} + +bool Logger::debugEnable(const std::string& dbg) { + try { + debugEnable(logging::DebugStream::streamForName(dbg)); + return true; + } catch ( std::out_of_range& ) { + return false; + } +} + +bool Logger::debugDisable(const std::string& dbg) { + try { + debugDisable(logging::DebugStream::streamForName(dbg)); + return true; + } catch ( std::out_of_range& ) { + return false; + } +} + +void Logger::log(logging::Level level, const std::string& msg, const Location& l) { + report(_output_std, level, 0, "", msg, l); +} + +void Logger::info(const std::string& msg, const Location& l) { + report(_output_std, logging::Level::Info, 0, "", msg, l); +} + +void Logger::warning(const std::string& msg, const Location& l) { + report(_output_std, logging::Level::Warning, 0, "", msg, l); + ++_warnings; +} + +void Logger::error(const std::string& msg, const Location& l) { error(msg, {}, l); } + +void Logger::error(const std::string& msg, const std::vector& context, const Location& l) { + report(_output_std, logging::Level::Error, 0, "", msg, l); + + for ( const auto& x : context ) + report(_output_std, logging::Level::Error, 0, "", util::fmt(" %s", x), l); + + ++_errors; +} + +void Logger::fatalError(const std::string& msg, const Location& l) { + report(_output_std, logging::Level::FatalError, 0, "", msg, l); + exit(1); +} + +void Logger::internalError(const std::string& msg, const Location& l) { + report(_output_std, logging::Level::InternalError, 0, "", msg, l); + util::abort_with_backtrace(); +} + +void Logger::_debug(const logging::DebugStream& dbg, const std::string& msg, const Location& l) { + if ( auto i = _debug_streams.find(dbg); i != _debug_streams.end() ) + report(_output_debug, logging::Level::Debug, i->second, dbg.name(), msg, l); +} + +void Logger::report(std::ostream& output, logging::Level level, int indent, const std::string& addl, + const std::string& msg, const Location& l) const { + std::string level_str = logging::to_string(level); + std::string indent_str = std::string(indent * 2, ' '); + + if ( level == logging::Level::Debug ) + level_str = util::fmt("debug/%s", addl); + + if ( l ) + output << util::fmt("[%s] %s%s: %s", level_str, indent_str, std::string(l), msg) << std::endl; + else + output << util::fmt("[%s] %s%s", level_str, indent_str, msg) << std::endl; +} diff --git a/hilti/src/base/timing.cc b/hilti/src/base/timing.cc new file mode 100644 index 000000000..66f1151c2 --- /dev/null +++ b/hilti/src/base/timing.cc @@ -0,0 +1,98 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace util; +using namespace util::timing; +using namespace util::timing::detail; + +using util::fmt; + +static std::string prettyTime(Duration d) { + static const std::vector> units = {{"w ", 1e9 * 60 * 60 * 24 * 7}, + {"d ", 1e9 * 60 * 60 * 24}, + {"hr", 1e9 * 60 * 60}, + {"m ", 1e9 * 60}, + {"s ", 1e9}, + {"ms", 1e6}, + {"us", 1e3}, + {"ns", 1}}; + + auto x = std::chrono::duration_cast(d); + + if ( x.count() == 0 ) + return "0s"; + + for ( const auto& [unit, factor] : units ) { + if ( x.count() >= factor ) + return fmt("%.2f%s", x.count() / factor, unit); + } + + cannot_be_reached(); +}; + +static std::string prettyTimeForUnit(Duration d, double factor, const std::string& unit) { + auto x = std::chrono::duration_cast(d); + return fmt("%.2f%s", x.count() / factor, unit); +} + +std::shared_ptr Manager::singleton() { + static std::shared_ptr singleton; + + if ( ! singleton ) + singleton = std::shared_ptr(new Manager()); + + return singleton; +} + +void Manager::register_(Ledger* ledger) { + if ( _all_ledgers.find(ledger->name()) != _all_ledgers.end() ) + hilti::logger().internalError(fmt("ledger %s already exists", ledger->name())); + + _all_ledgers[ledger->name()] = ledger; +} + + +void Manager::unregister(Ledger* ledger) { _all_ledgers.erase(ledger->name()); } + +Ledger* Manager::newLedger(const std::string& name) { + if ( auto i = _all_ledgers.find(name); i != _all_ledgers.end() ) + return i->second; + + _our_ledgers.emplace_back(name); + return &_our_ledgers.back(); +} + +void Manager::summary(std::ostream& out) { + auto mgr = singleton(); + + if ( mgr->_all_ledgers.empty() ) { + out << "=== No timing information recorded." << std::endl; + return; + } + + std::list sorted_ledgers; + + for ( const auto& [name, ledger] : mgr->_all_ledgers ) { + if ( ledger->_num_completed == 0 ) + continue; + + sorted_ledgers.emplace_back(ledger); + } + + sorted_ledgers.sort([](const auto& x, const auto& y) { return x->_time_used.count() > y->_time_used.count(); }); + + auto total_time = (Clock::now() - mgr->_created); + + out << "\n=== Execution Time Summary ===\n\n"; + + for ( auto ledger : sorted_ledgers ) + out << fmt("%7.2f%% ", (100.0 * ledger->_time_used.count() / total_time.count())) + << fmt("%8s", prettyTimeForUnit(ledger->_time_used, 1e9, "s")) << " " << ledger->_name << " " + << fmt("(#%" PRIu64 ")", ledger->_num_completed) << "\n"; + + out << "\nTotal time: " << prettyTime(total_time) << "\n"; + out << std::endl; +} diff --git a/hilti/src/base/type_erase.cc b/hilti/src/base/type_erase.cc new file mode 100644 index 000000000..6e7adfdda --- /dev/null +++ b/hilti/src/base/type_erase.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include + +using namespace util; + +#ifdef HILTI_TYPE_ERASURE_PROFILE +namespace util::type_erasure::detail { +inline bool operator<(const Counters& x, const Counters& y) { return x.max >= y.max; } +} // namespace util::type_erasure::detail +#endif + +void type_erasure::summary(std::ostream& out) { +#ifdef HILTI_TYPE_ERASURE_PROFILE + const auto& unordered = type_erasure::detail::instance_counters(); + + int64_t total_max = 0; + int64_t total_current = 0; + + for ( const auto& [k, v] : unordered ) { + total_max += v.max; + total_current += v.current; + } + + std::set> ordered; + + for ( const auto& [k, v] : unordered ) + ordered.insert(std::make_pair(v, k)); + + out << "\n=== Top-20 type-erased instances (#max/#min)\n\n"; + + int count = 20; + for ( const auto& [v, k] : ordered ) { + if ( v.max >= 100 ) + out << fmt("%40s %10d (%5.2f) %10d (%5.2f)\n", util::demangle(k), v.max, (100.0 * v.max / total_max), + v.current, (100.0 * v.current / total_current)); + + if ( --count == 0 ) + break; + } + + out << "\n"; +#else + out << "\n (No support for type-erase profiling compiled in.)\n\n"; +#endif +} diff --git a/hilti/src/base/util.cc b/hilti/src/base/util.cc new file mode 100644 index 000000000..793dfbcb1 --- /dev/null +++ b/hilti/src/base/util.cc @@ -0,0 +1,281 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace util; + +void detail::__internal_error(const std::string& s) { hilti::logger().internalError(s); } + +void util::cannot_be_reached() { hilti::logger().internalError("code is executing that should not be reachable"); } + +std::vector util::split(std::string s, const std::string& delim) { + std::vector l; + + while ( true ) { + size_t p = s.find(delim); + + if ( p == std::string::npos ) + break; + + l.push_back(s.substr(0, p)); + + // FIXME: Don't understand why directly assigning to s doesn't work. + std::string t = s.substr(p + delim.size(), std::string::npos); + s = t; + } + + l.push_back(s); + return l; +} + +std::pair util::split1(std::string s, const std::string& delim) { + if ( auto i = s.find(delim); i != std::string::npos ) + return std::make_pair(s.substr(0, i), s.substr(i + delim.size())); + + return std::make_pair(std::move(s), ""); +} + +std::pair util::rsplit1(std::string s, const std::string& delim) { + if ( auto i = s.rfind(delim); i != std::string::npos ) + return std::make_pair(s.substr(0, i), s.substr(i + delim.size())); + + return std::make_pair("", std::move(s)); +} + +std::string util::replace(const std::string& s, const std::string& o, const std::string& n) { + if ( o.empty() ) + return s; + + auto x = s; + + size_t i = 0; + while ( (i = x.find(o, i)) != std::string::npos ) { + x.replace(i, o.length(), n); + i += n.length(); + } + + return x; +} + +std::string util::tolower(const std::string& s) { + std::string t = s; + std::transform(t.begin(), t.end(), t.begin(), ::tolower); + return t; +} + +std::string util::toupper(const std::string& s) { + std::string t = s; + std::transform(t.begin(), t.end(), t.begin(), ::toupper); + return t; +} + +std::string util::rtrim(const std::string& s) { + auto t = s; + t.erase(std::find_if(t.rbegin(), t.rend(), [](char c) { return ! std::isspace(c); }).base(), t.end()); + return t; +} + +std::string util::ltrim(const std::string& s) { + auto t = s; + t.erase(t.begin(), std::find_if(t.begin(), t.end(), [](char c) { return ! std::isspace(c); })); + return t; +} + +std::string util::trim(const std::string& s) { return ltrim(rtrim(s)); } + +uint64_t util::hash(const std::string& str) { return util::hash(str.data(), str.size()); } + +uint64_t util::hash(const char* data, size_t len) { + uint64_t h = 0; + + while ( len-- ) + h = (h << 5U) - h + static_cast(*data++); + + return h; +} + +std::string util::uitoa_n(uint64_t value, unsigned int base, int n) { + static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + assert(base <= strlen(dig)); + + std::string s; + + do { + s.append(1, dig[value % base]); + value /= base; + } while ( value && (n < 0 || s.size() < static_cast(n) - 1) ); + + return s; +} + +bool util::endsWith(const std::string& s, const std::string& suffix) { + size_t i = s.rfind(suffix); + + if ( i == std::string::npos ) + return false; + + return (i == (s.length() - suffix.size())); +} + +hilti::Result util::findInPaths(const std::filesystem::path& file, + const std::vector& paths) { + if ( file.is_absolute() ) { + if ( std::filesystem::exists(file) ) + return file; + + return hilti::result::Error(fmt("absolute path %s does not exist", file)); + } + + for ( const auto& d : paths ) { + auto p = d / file; + if ( std::filesystem::exists(p) ) + return p; + } + + return hilti::result::Error(fmt("%s not found", file)); +} + +std::filesystem::path util::normalizePath(const std::filesystem::path& p) { + if ( p.empty() ) + return ""; + + if ( ! std::filesystem::exists(p) ) + return p; + + char buffer[PATH_MAX]; + return realpath(std::filesystem::absolute(p).native().c_str(), buffer); +} + +hilti::Result util::createTemporaryFile(const std::string& prefix) { + std::error_code ec; + auto tmp_dir = std::filesystem::temp_directory_path(ec); + + if ( ec ) + return hilti::result::Error(fmt("could not create temporary file: %s", ec.message())); + + auto template_ = (tmp_dir / (prefix + "-XXXXXX")).native(); + + auto handle = ::mkstemp(template_.data()); + if ( handle == -1 ) + return hilti::result::Error(fmt("could not create temporary file in '%s': %s", tmp_dir, strerror(errno))); + + ::close(handle); + + return std::filesystem::path(template_); +} + +std::filesystem::path util::currentExecutable() { return normalizePath(::FindExecutable()); } + +void util::abort_with_backtrace() { + std::cerr << "\n--- Aborting" << std::endl; + hilti::rt::Backtrace bt; + for ( const auto& f : bt.backtrace() ) + std::cerr << f << std::endl; + abort(); +} + +double util::currentTime() { + struct timeval tv {}; + gettimeofday(&tv, nullptr); + return double(tv.tv_sec) + double(tv.tv_usec) / 1e6; +} + +std::string util::toIdentifier(const std::string& s, bool ensure_non_keyword) { + static char const* const hex = "0123456789abcdef"; + + if ( s.empty() ) + return s; + + std::string normalized = s; + + normalized = replace(normalized, "::", "_"); + normalized = replace(normalized, ":", "_"); + normalized = replace(normalized, ">", "_"); + normalized = replace(normalized, ",", "_"); + normalized = replace(normalized, ".", "_"); + normalized = replace(normalized, " ", "_"); + normalized = replace(normalized, "-", "_"); + normalized = replace(normalized, "'", "_"); + normalized = replace(normalized, "\"", "_"); + normalized = replace(normalized, "__", "_"); + + while ( ::util::endsWith(normalized, "_") ) + normalized = normalized.substr(0, normalized.size() - 1); + + std::string ns; + + for ( unsigned char c : normalized ) { + if ( isalnum(c) || c == '_' ) { + ns += c; + continue; + } + + ns += "x"; + ns += hex[c >> 4U]; + ns += hex[c % 0x0f]; + } + + ns = replace(ns, "__", "_"); + + if ( isdigit(ns[0]) ) + ns = "_" + ns; + + if ( ensure_non_keyword ) + ns += "_"; + + return ns; +} + +std::string util::prefixParts(const std::string& in, const std::string& prefix, const std::string& include_tag) { + if ( in.empty() ) + return ""; + + auto x = transform(split(in, " "), [&](auto s) { + if ( s.empty() ) + return std::string(); + + if ( include_tag.size() ) { + auto x = split(s, "!"); + if ( x.size() == 3 ) { + if ( x[1] != include_tag ) + return std::string(); + + s = x[2]; + } + } + + return prefix + trim(s); + }); + + return join(filter(x, [](auto s) -> bool { return s.size(); }), " "); +} + +std::vector util::flattenParts(const std::vector& in) { + std::vector out; + + for ( const auto& i : in ) { + for ( auto s : util::split(i) ) { + s = util::trim(s); + if ( s.empty() ) + continue; + + out.push_back(s); + } + } + + return out; +} diff --git a/hilti/src/compiler/clang.cc b/hilti/src/compiler/clang.cc new file mode 100644 index 000000000..a5bcd8caf --- /dev/null +++ b/hilti/src/compiler/clang.cc @@ -0,0 +1,764 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +// This file contains the implementation of Hilti’s JIT compiler for C++ code. +// The compiler takes C++ code generated by Hilti, compiles it down to LLVM IR +// using Clang, and emits a shared library using the system linker which +// finally gets loaded into the process. +// +// In order to compile C++ down to LLVM IR the compiler spins up Clang’s +// `CompilerInstance` for each source file given to it using standard clang +// command line arguments. Once the LLVM IR has been generated, we link it +// together into one Module using LLVM’s `Linker` class. Finally we use Clang +// to turn the linked module in a shared library on disk using the system +// linker. This shared library is loaded into the process with `::dlopen`. + +#include "hilti/compiler/detail/clang.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Leak checker API +#ifdef HILTI_HAVE_SANITIZER +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hilti::logging::debug { +inline const DebugStream Driver("driver"); +} // namespace hilti::logging::debug + +using namespace hilti; +using namespace hilti::detail; + +#ifdef __APPLE__ +#define SHARED_LIBRARY_EXTENSION ".dylib" +#else +#define SHARED_LIBRARY_EXTENSION ".so" +#endif + +// PIMPL backend class +// +// Performance Notes(zeke): +// +// At the moment our methodology focuses more on making this work at all than +// making it work elegantly. Such is the nature of getting clang to work this +// way, but I think we could make this markedly more performant. +// +// Presently we compile every source file with a new CompilerInstance. This +// process of spinning up and then spinning down a CompilerInstance for every +// new bit of C++ code we see is very slow and dominates JIT performance. +// Luckily, clang's CompilerInstance has an incremental parsing mode where it +// appears that it can parse multiple C++ source files without trouble. There +// is an example of this in the clang repo and I think that that would be a +// great place to start exploring making this code faster. +struct ClangJIT::Implementation { // NOLINT + Implementation(std::shared_ptr context); + ~Implementation(); + + /** See `ClangJit::compile()`. */ + bool compile(const std::string& file, std::optional code); + + /** See `ClangJit::jit()`. */ + Result jit(); + + /** See `ClangJit::retrieveLibrary()`. */ + std::optional> retrieveLibrary() const; + + /** See `ClangJit::setDumpCode()`. */ + void setDumpCode() { dump_code = true; } + + /** See `ClangJit::initRuntime()`. */ + bool initRuntime(); + + /** See `ClangJit::finishRuntime()`. */ + bool finishRuntime(); + + /** Returns the compiler options in use. */ + auto options() const { return context->options(); } + +private: + /** + * Links all previously compiled, individual LLVM modules into a joint + * LLVM module. + * + * @return the linked, joint module, or null i an error occured + */ + std::pair, std::string> link(); + + /** + * Saves an LLVM bitcode file to disk. + * + * @param module bitcode to save + * @param path path to file to write to + */ + void saveBitcode(const llvm::Module& module, std::string path); + + /** + * Compiles module to a native shared library. + * + * @param LLVM module to save + * @return path to the created library + */ + Result compileModule(llvm::Module&& module); + + /* + * Creates an invocation for Clang's ``CompilerInstance`` class from its + * arguments and ``-fsyntax-only`` using the provided driver. The + * arguments should be in the same form as traditional clang arguments. + * + * @param args arguments to be passed to clang. + * @param id the name of the current file / module being + * compiled. Used for debugging purposes. + * @param compiler_diags the diagnostics engine that the + * CompilerInstance this is creating an invocation for will use. + * + * @return a CompilerInvocation ready for use with a CompilerInstance. + */ + std::unique_ptr createInvocationFromCommandLine( + std::vector args, const std::string& id, clang::DiagnosticsEngine& compiler_diags); + + /** Runs code optimization passes on a module. */ + void optimizeModule(llvm::Module* m); + + /** + * Adapts visilibity of global symbols in the final linked LLVM module. + * This in particular internalizes symbols to the degree we can, and + * renames selected exported symbols to have globally unique names. + * + * @param module LLVM module to operate on + * @param symbols_to_uniquify symbols to rewrite to be globally unique, + * even across runs; they will also be exported if they aren't already. + * + * @return a map of old-to-new symbols names for those in + * ``symbols_to_uniquify``. + */ + std::map adaptSymbolVisibility(llvm::Module& module, + const std::set& symbols_to_uniquify); + + // HILTI context to pull settings from. + std::shared_ptr context; + + // The context for compilation and the JIT. Manages global state + // over added modules. + llvm::orc::ThreadSafeContext shared_context; + + // Adds definitions to our JIT so that it can properly link and + // manage C++ code and does teardown when the JIT is done. + llvm::orc::LocalCXXRuntimeOverrides runtime_overrides; + + // FIFO queue of Modules to be just in timed. The frontend class + // will push Modules to this as it compiles C++ source files. + std::queue> module_queue; + + bool dump_code = false; + + std::optional shared_library; + + /* + * Clones a llvm::Module to a new context. This effectively moves + * control/management of the module's global state into the target + * context. + * + * @param m the module to be cloned. + * @param target the context that the module should be cloned + * into. + * + * @return a new module who's global global data is managed by the + * target context.. + */ + static std::unique_ptr cloneToContext(std::unique_ptr m, + llvm::LLVMContext& target); // NOLINT +}; + +ClangJIT::Implementation::Implementation(std::shared_ptr context) + : context(context), shared_context(std::make_unique()) { + // Initialize LLVM enviroment. + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); +} + +ClangJIT::Implementation::~Implementation() { llvm::llvm_shutdown(); } + +bool ClangJIT::Implementation::compile(const std::string& file, std::optional code) { + util::timing::Collector _("hilti/jit/clang/compile"); + + // Build standard clang++ arguments. + std::vector args = {hilti::configuration().jit_clang_executable}; + + if ( options().debug ) + args = util::concat(args, hilti::configuration().runtime_cxx_flags_debug); + else + args = util::concat(args, hilti::configuration().runtime_cxx_flags_release); + + for ( const auto& i : options().cxx_include_paths ) { + args.emplace_back("-I"); + args.push_back(i); + } + + // For debug output on compilation: + // args.push_back("-v"); + // args.push_back("-###"); + + if ( auto dir = hilti::configuration().jit_clang_resource_dir; ! dir.empty() ) { + args.emplace_back("-resource-dir"); + args.emplace_back(dir.native()); + } + + for ( auto dir : util::split(hilti::configuration().jit_c_system_include_dirs, ":") ) { + args.emplace_back("-isystem"); + args.emplace_back(dir); + } + + for ( auto dir : util::split(hilti::configuration().jit_cxx_system_include_dirs, ":") ) { + args.emplace_back("-isystem"); + args.emplace_back(dir); + } + + args.push_back(file); + + // Reusing the driver across calls gives us trouble. In a perfect world, + // we should be able to get around that by spinning up a compilerinstance + // with incremental processing enabled and reusing that. For now though, + // we are content with just creating a new compilerinstance and driver at + // every call to compiler. + // + // If we would like to make compilation faster, I very much + // beleive that this is a good place to start. + HILTI_DEBUG(logging::debug::Jit, util::fmt("creating driver (%s)", util::join(args, " "))); + + clang::CompilerInstance clang_; + + clang_.createDiagnostics(); + if ( ! clang_.hasDiagnostics() ) { + logger().error("jit: failed to create compilation diagnostics"); + return false; + } + + auto ci = createInvocationFromCommandLine(args, file, clang_.getDiagnostics()); + + if ( code ) { + auto buffer = llvm::MemoryBuffer::getMemBufferCopy(*code); + ci->getPreprocessorOpts().addRemappedFile(file, buffer.release()); + } + + clang_.setInvocation(std::move(ci)); + + // Force Clang to release its memory rather than reyling on process + // termination to clean up. + clang_.getFrontendOpts().DisableFree = false; + + // Aquire a lock on the context. + auto lock = shared_context.getLock(); + auto action = std::make_unique(); + + { +#ifdef HILTI_HAVE_SANITIZER + // TODO(robin): Something in LLVM is leaking but I can't figure out + // how to stop it. For now we just don't track anything allocated + // during compilation in this block here. + __lsan::ScopedDisabler llvm_leaks; +#endif + + if ( ! clang_.ExecuteAction(*action) ) { + logger().error("jit: failed to execute compilation action."); + return false; + } + } + + std::unique_ptr m = action->takeModule(); + if ( ! m ) { + logger().error("jit: failed to generate LLVM IR for module"); + return false; + } + + // Clone to shared context and move into queue. Not doing so causes seg + // faults in hilti when it attempts certian lookups. + module_queue.push(cloneToContext(std::move(m), *shared_context.getContext())); + return true; +} + +std::pair, std::string> ClangJIT::Implementation::link() { + util::timing::Collector _("hilti/jit/clang/link"); + + // First, link all the modules in the queue together. Not doing this + // causes illegal LLVM IR to be added to the JIT because we compile every + // C++ source file independently. + auto linked_module = std::make_unique("__LINKED__", *shared_context.getContext()); + llvm::Linker linker(*linked_module); + + while ( module_queue.size() ) { + auto module = std::move(module_queue.front()); + module_queue.pop(); + + if ( dump_code ) + saveBitcode(*module, util::fmt("dbg.%s.bc", module->getModuleIdentifier())); + + if ( linker.linkInModule(std::move(module), llvm::Linker::Flags::None) ) { + logger().error("jit: error linking bitcode modules"); + return std::make_pair(nullptr, ""); + } + } + + // Collect symbols that need to we need to rename to be globally + // unique because (1) they must remain externally visible, and (2) we + // may see them in more than one object file. + std::set symbols_to_uniquify = {"__linker__"}; + std::set symbols_to_expose = {"hilti_main"}; + + std::string one_ctor; // remembers one arbitrarty ctor for below + + for ( const auto& x : llvm::orc::getConstructors(*linked_module) ) { + one_ctor = x.Func->getName().str(); + symbols_to_uniquify.insert(x.Func->getName().str()); + } + + for ( const auto& x : llvm::orc::getDestructors(*linked_module) ) + symbols_to_uniquify.insert(x.Func->getName().str()); + + // Do the renaming. This returns a map mapping old names to the new + // uniquified names. + auto symbol_mapping = adaptSymbolVisibility(*linked_module, symbols_to_uniquify); + auto unique_symbols = util::map_values(symbol_mapping); + + // Internalize all symbols that don't need to be externally visible. This + // is adapted from code in llvm-link.cpp. + auto must_preserve = [&](const llvm::GlobalValue& GV) { + if ( GV.hasName() ) { + if ( unique_symbols.find(std::string(GV.getName())) != unique_symbols.end() ) + return true; // don't internalize + + if ( symbols_to_expose.find(std::string(GV.getName())) != symbols_to_expose.end() ) + return true; // don't internalize + } + + return ! GV.hasName(); + }; + + llvm::InternalizePass(must_preserve).internalizeModule(*linked_module); + + if ( dump_code ) + saveBitcode(*linked_module, util::fmt("dbg.%s.bc", linked_module->getModuleIdentifier())); + + // We need to pick one externally visible symbol form the module, as + // that's what we'll use to trigger materilization. + auto all_exported_symbols = util::set_union(unique_symbols, symbols_to_expose); + std::string linker_symbol = all_exported_symbols.size() ? *all_exported_symbols.begin() : std::string(); + + return std::make_pair(std::move(linked_module), linker_symbol); +} + +Result ClangJIT::Implementation::jit() { + util::timing::Collector _("hilti/jit/clang/jit"); + + if ( module_queue.size() ) { + // Link all the pending modules together. + auto [linked_module, linker_symbol] = link(); + if ( ! linked_module ) + return result::Error("jit: linking failed"); + + if ( linker_symbol.empty() ) { + HILTI_DEBUG(logging::debug::Jit, "skipping empty linked module"); + return Nothing(); + } + + std::string str; + llvm::raw_string_ostream stream(str); + if ( llvm::verifyModule(*linked_module, &stream) ) + return result::Error(util::fmt("jit: linked module failed verification (%s)", stream.str())); + + if ( options().optimize ) + optimizeModule(linked_module.get()); + + auto library = compileModule(std::move(*linked_module)); + if ( ! library ) { + logger().fatalError(util::fmt("could not create library: %s", library.error())); + } + + shared_library.emplace(std::move(*library)); + + // Load the created library. + if ( auto load = shared_library->open(); ! load ) { + return load.error(); + } + + if ( dump_code ) { + constexpr char id[] = "__LINKED__"; + std::string path = std::string("dbg.") + id + SHARED_LIBRARY_EXTENSION; + // Logging to driver because that's where all the other "saving to ..." messages go. + HILTI_DEBUG(logging::debug::Driver, util::fmt("saving shared library for LLVM module %s to %s", id, path)); + if ( auto success = shared_library->save(path); ! success ) { + return success; + } + } + } + + return Nothing(); +} + +void ClangJIT::Implementation::optimizeModule(llvm::Module* m) { + HILTI_DEBUG(logging::debug::Jit, util::fmt("optimizing module %s", m->getModuleIdentifier())); + + auto fpm = std::make_unique(m); + // These are the reccomended optimization passes from + // LLVM's build a JIT tutorial. + fpm->add(llvm::createInstructionCombiningPass()); + fpm->add(llvm::createReassociatePass()); + fpm->add(llvm::createGVNPass()); + fpm->add(llvm::createCFGSimplificationPass()); + fpm->doInitialization(); + + // Run the optimizations over all functions in the module being added to + // the JIT. + for ( auto& func : *m ) + fpm->run(func); +} + +std::map ClangJIT::Implementation::adaptSymbolVisibility( + llvm::Module& module, const std::set& symbols_to_uniquify) { + std::map new_symbols; + + auto process_global_value = [&](auto& v) { + if ( v.isDeclaration() ) + return; + + if ( v.hasName() && symbols_to_uniquify.find(v.getName().str()) != symbols_to_uniquify.end() ) { + // Make symbol (hopefully) unique by including its address into the name. + std::string new_symbol = util::fmt("%s.%p", v.getName().str(), &module); + new_symbols[std::string(v.getName())] = new_symbol; + v.setName(new_symbol); + + // LLVM emits constructors as internal but we need to look them + // up. Seems safe to adapt all uniquified symbols as they + // presumably they should all be exported. + if ( v.getLinkage() == llvm::GlobalValue::InternalLinkage ) + v.setLinkage(llvm::GlobalValue::ExternalLinkage); + } + }; + + // The loops here follows https://stackoverflow.com/a/45323753 + for ( auto it = module.global_begin(); it != module.global_end(); ++it ) + process_global_value(*it); + + for ( auto it = module.alias_begin(); it != module.alias_end(); ++it ) + process_global_value(*it); + + for ( auto& F : module ) + process_global_value(F); + + return new_symbols; +} + +std::optional> ClangJIT::Implementation::retrieveLibrary() const { + if ( ! shared_library ) + return std::nullopt; + return *shared_library; +} + +bool ClangJIT::Implementation::initRuntime() { + util::timing::Collector _("hilti/jit/clang/init-runtime"); + return true; +} + +bool ClangJIT::Implementation::finishRuntime() { + util::timing::Collector _("hilti/jit/clang/finish-runtime"); + return true; +} + +void ClangJIT::Implementation::saveBitcode(const llvm::Module& module, std::string path) { + // Logging to driver because that's where all the other "saving to ..." messages go. + HILTI_DEBUG(logging::debug::Driver, + util::fmt("saving bitcode for LLVM module %s to %s", module.getModuleIdentifier(), path)); + std::ofstream out(path); + llvm::raw_os_ostream lout(out); + llvm::WriteBitcodeToFile(module, lout); + lout.flush(); + out.flush(); +} + +Result ClangJIT::Implementation::compileModule(llvm::Module&& module) { + util::timing::Collector _("hilti/jit/clang/save_library"); + +#ifdef HILTI_HAVE_SANITIZER + // TODO(robin): Something in LLVM is leaking but I can't figure out + // how to stop it. For now we just don't track anything allocated + // during compilation in this block here. + __lsan::ScopedDisabler llvm_leaks; +#endif + + // A helper function to remove temporary files on scope exit. + auto cleanup_on_exit = [](const std::filesystem::path& tempfile) { + return llvm::make_scope_exit([&]() { + std::error_code ec; + std::filesystem::remove(tempfile, ec); + + if ( ec ) { + logger().error(util::fmt("cleanup of file %s failed: %s", tempfile, ec)); + } + }); + }; + + // Save object code to a temporary file. + auto object_path = util::createTemporaryFile(std::string(module.getName())); + + if ( ! object_path ) + return object_path.error(); + + auto c1 = cleanup_on_exit(*object_path); + + const auto& triple = module.getTargetTriple(); + HILTI_DEBUG(logging::debug::Jit, util::fmt("creating library with target triple '%s'", triple)); + + std::string message; + auto target = llvm::TargetRegistry::lookupTarget(triple, message); + if ( ! target ) { + return result::Error(util::fmt("could not look up target: %s", message)); + } + + auto machine = target->createTargetMachine(triple, "generic", "", llvm::TargetOptions(), llvm::Reloc::Model::PIC_); + + if ( context->options().optimize ) + machine->setOptLevel(llvm::CodeGenOpt::Aggressive); + + llvm::legacy::PassManager manager; + + std::error_code error; + llvm::raw_fd_ostream file(object_path->native(), error); + if ( error ) + return result::Error(util::fmt("could not open object file %s: %s", object_path, error.message())); + +#if LLVM_VERSION_MAJOR < 10 +#define CGFT_OBJECTFILE llvm::TargetMachine::CGFT_ObjectFile +#else +#define CGFT_OBJECTFILE llvm::CGFT_ObjectFile +#endif + if ( machine->addPassesToEmitFile(manager, file, nullptr, CGFT_OBJECTFILE) ) + return result::Error("adding passes failed"); + + if ( ! manager.run(module) ) + return result::Error(util::fmt("object file %s could not be created", object_path)); + + file.close(); + + // Use clang to link the object file into a shared library. + clang::CompilerInstance clang_; + clang_.createDiagnostics(); + if ( ! clang_.hasDiagnostics() ) { + return result::Error("jit: failed to create compilation diagnostics"); + } + + auto library_path = util::createTemporaryFile(util::fmt("%s.hlto", module.getName().str())); + if ( ! library_path ) + return library_path.error(); + + auto c2 = cleanup_on_exit(*library_path); + + auto& diagnostics = clang_.getDiagnostics(); + + std::vector args = {hilti::configuration().jit_clang_executable, + "-shared", + "-Wl,-undefined", + "-Wl,dynamic_lookup", + *object_path, + "-o", + *library_path}; + + auto driver = std::make_unique(args[0], llvm::sys::getDefaultTargetTriple(), diagnostics); + + HILTI_DEBUG(logging::debug::Jit, + util::fmt("compiling shared library %s with flags: %s", *library_path, util::join(args))); + + auto cargs = util::transform(args, [](auto& s) -> const char* { return s.c_str(); }); + auto compilation = std::unique_ptr(driver->BuildCompilation(cargs)); + + llvm::SmallString<1024> job_description; + + for ( const auto& job : compilation->getJobs() ) { + llvm::raw_svector_ostream stream(job_description); + job.Print(stream, "", false); + HILTI_DEBUG(logging::debug::Jit, util::fmt("executing job for linking module: %s", job_description.c_str())); + + std::string error; + bool failed = false; + job.Execute({}, &error, &failed); + + if ( failed ) + return result::Error(util::fmt("could not create shared object: %s", error)); + } + + return Library(std::filesystem::absolute(*library_path)); +} + +std::unique_ptr ClangJIT::Implementation::cloneToContext(std::unique_ptr m, + llvm::LLVMContext& target) { + using namespace llvm; + + // BitcodeWriter requires that the Module has been materialized. + if ( auto error = m->materializeAll() ) { + logger().error("jit: failed to materialize module."); + return nullptr; + } + + SmallVector ClonedModuleBuffer; + BitcodeWriter BCWriter(ClonedModuleBuffer); + BCWriter.writeModule(*m); + BCWriter.writeSymtab(); + BCWriter.writeStrtab(); + + MemoryBufferRef ClonedModuleBufferRef(StringRef(ClonedModuleBuffer.data(), ClonedModuleBuffer.size()), + "cloned module buffer"); + + auto ClonedModule = llvm::cantFail(parseBitcodeFile(ClonedModuleBufferRef, target)); + + ClonedModule->setModuleIdentifier(m->getName()); + return ClonedModule; +} + +std::unique_ptr ClangJIT::Implementation::createInvocationFromCommandLine( + std::vector args, const std::string& id, clang::DiagnosticsEngine& compiler_diags) { + // FIXME: We shouldn't have to pass in the path info. Not doing so though + // causes the compiler to fail to find headers. + auto driver = std::make_unique(args[0], llvm::sys::getDefaultTargetTriple(), compiler_diags); + + // Don't check that inputs exist, they may have been remapped. + driver->setCheckInputsExist(false); + + // FIXME: Find a cleaner way to force the driver into restricted modes. + args.emplace_back("-fsyntax-only"); + + // Compile position-independent code so we can use the code in a shared library later. + args.emplace_back("-fPIC"); + + std::vector cargs = util::transform(args, [](auto& s) -> const char* { return s.c_str(); }); + std::unique_ptr C(driver->BuildCompilation(cargs)); + if ( ! C ) + logger().fatalError("failed to build JIT compilation"); + + // Print the cc1 options if -### was present. + if ( C->getArgs().hasArg(clang::driver::options::OPT__HASH_HASH_HASH) ) + C->getJobs().Print(llvm::errs(), "\n", true); + + // We expect to get back exactly one command job, if we didn't something + // failed. + const clang::driver::JobList& jobs = C->getJobs(); + + if ( jobs.empty() ) + logger().internalError("jit: no job in compilation"); + + if ( jobs.size() > 1 ) + logger().internalError("jit: more than the expected 1 job in compilation"); + + const clang::driver::Command& Cmd = llvm::cast(*jobs.begin()); + if ( std::string(Cmd.getCreator().getName()) != "clang" ) + logger().internalError("jit: unexpected job type in compilation"); + + const llvm::opt::ArgStringList& CCArgs(Cmd.getArguments()); + + HILTI_DEBUG(logging::debug::Jit, util::fmt("compiling module %s with \"%s\"", id, util::join(CCArgs, " "))); + + // clang::CompilerInvocation::Cr + + auto CI = std::make_unique(); + +#if LLVM_VERSION_MAJOR < 10 + if ( ! clang::CompilerInvocation::CreateFromArgs(*CI, const_cast(CCArgs.data()), + const_cast(CCArgs.data()) + CCArgs.size(), + compiler_diags) ) +#else + if ( ! clang::CompilerInvocation::CreateFromArgs(*CI, CCArgs, compiler_diags) ) +#endif + logger().internalError("failed to create JIT compiler invocation"); + + return CI; +} + +ClangJIT::ClangJIT(std::shared_ptr context) : _impl(new ClangJIT::Implementation(context)) {} + +ClangJIT::~ClangJIT() = default; // Needed here to allow ClangJIT to be forwarded declared. + +std::string ClangJIT::compilerVersion() { return clang::getClangFullVersion(); } + +bool ClangJIT::compile(const CxxCode& code) { return _impl->compile(util::fmt("%s.cc", code.id()), code.code()); } + +bool ClangJIT::compile(const std::filesystem::path& p) { return _impl->compile(p, {}); } + +Result ClangJIT::jit() { return _impl->jit(); } + +bool ClangJIT::initRuntime() { return _impl->initRuntime(); } + +bool ClangJIT::finishRuntime() { return _impl->finishRuntime(); } + +std::optional> ClangJIT::retrieveLibrary() const { + return _impl->retrieveLibrary(); +} + +void ClangJIT::setDumpCode() { _impl->setDumpCode(); } diff --git a/hilti/src/compiler/codegen/codegen.cc b/hilti/src/compiler/codegen/codegen.cc new file mode 100644 index 000000000..681eb9cfc --- /dev/null +++ b/hilti/src/compiler/codegen/codegen.cc @@ -0,0 +1,552 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +using namespace hilti::detail; +using namespace hilti::detail::codegen; + +namespace { + +struct GlobalsVisitor : hilti::visitor::PreOrder { + explicit GlobalsVisitor(CodeGen* cg) : cg(cg) {} + + GlobalsVisitor(const GlobalsVisitor&) = delete; + GlobalsVisitor(GlobalsVisitor&&) noexcept = delete; + + CodeGen* cg; + std::vector globals; + std::vector constants; + + static void addDeclarations(CodeGen* cg, const Node& module, const ID& module_id, cxx::Unit* unit, + bool include_implementation) { + auto v = GlobalsVisitor(cg); + for ( auto i : v.walk(module) ) + v.dispatch(i); + + if ( v.globals.empty() && v.constants.empty() ) + return; + + auto ns = cxx::ID(cg->options().cxx_namespace_intern, module_id); + + for ( const auto& c : v.constants ) + unit->add(c); + + if ( ! v.globals.empty() ) { + if ( include_implementation ) + unit->setUsesGlobals(); + + auto t = cxx::declaration::Type{.id = {ns, "__globals_t"}, .type = cxx::Type(v.cxxGlobalsType())}; + + auto idx = + cxx::declaration::Global{.id = {ns, "__globals_index"}, .type = "unsigned int", .linkage = "inline"}; + + unit->add(idx); + unit->add(t); + + auto body = cxx::Block(); + body.addStatement("return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index)"); + + auto body_decl = cxx::declaration::Function{ + .result = "auto", + .id = {ns, "__globals"}, + .args = {}, + .linkage = "static", + .inline_body = body, + }; + + unit->add(body_decl); + } + + if ( include_implementation ) { + // Create the initGlobals() function. + auto id = cxx::ID{ns, "__init_globals"}; + + auto body_decl = + cxx::declaration::Function{.result = cg->compile(type::Void(), codegen::TypeUsage::FunctionResult), + .id = id, + .args = {{.id = "ctx", .type = "hilti::rt::Context*"}}, + .linkage = "extern"}; + + auto body = cxx::Block(); + cg->pushCxxBlock(&body); + + if ( ! v.globals.empty() ) + body.addStatement("hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index)"); + + for ( auto g : v.globals ) { + if ( g.init ) + body.addStatement(fmt("__globals()->%s = %s", g.id.local(), *g.init)); + else if ( g.args.size() ) + body.addStatement(fmt("__globals()->%s = {%s}", g.id.local(), util::join(g.args, ", "))); + } + + cg->popCxxBlock(); + + auto body_impl = cxx::Function{.declaration = body_decl, .body = std::move(body)}; + unit->add(body_decl); + unit->add(body_impl); + } + } + + cxx::type::Struct cxxGlobalsType() const { + std::vector fields; + + for ( const auto& g : globals ) { + auto f = cxx::declaration::Local{.id = g.id.local(), .type = g.type}; + fields.emplace_back(f); + } + + return cxx::type::Struct{.members = std::move(fields), .type_name = "__globals_t"}; + } + + void operator()(const declaration::GlobalVariable& n) { + auto args = util::transform(n.typeArguments(), [this](auto a) { return cg->compile(a); }); + auto init = n.init() ? cg->compile(*n.init()) : cg->typeDefaultValue(n.type()); + auto x = cxx::declaration::Global{.id = {cg->unit()->cxxNamespace(), n.id()}, + .type = cg->compile(n.type(), codegen::TypeUsage::Storage), + .args = std::move(args), + .init = std::move(init), + .linkage = "extern"}; + + globals.push_back(x); + } + + void operator()(const declaration::Constant& n) { + auto x = cxx::declaration::Global{.id = {cg->unit()->cxxNamespace(), n.id()}, + .type = "auto", + .init = cg->compile(n.value()), + .linkage = "const"}; + + constants.push_back(x); + } +}; + +struct Visitor : hilti::visitor::PreOrder { + Visitor(CodeGen* cg, const Scope& module_scope, cxx::Unit* unit) : cg(cg), unit(unit), module_scope(module_scope) {} + CodeGen* cg; + cxx::Unit* unit; + + std::optional module; + const Scope& module_scope; + + // Top-level nodes. + + void operator()(const Module& n) { + unit->setModule(n); + + for ( const auto& p : plugin::registry().plugins() ) { + for ( const auto& i : p.cxx_includes ) { + auto include = cxx::declaration::IncludeFile{i}; + unit->add(include); + } + } + + for ( const auto& i : n.moduleProperties("%cxx-include") ) { + if ( auto expr = i.expression() ) { + if ( auto ctor = expr->tryAs() ) { + if ( auto str = ctor->ctor().tryAs() ) { + auto include = cxx::declaration::IncludeFile{str->value()}; + unit->add(include); + continue; + } + } + } + + logger().error("%cxx-include must be used with a constant string"); + } + + auto src = n.id(); + + if ( n.meta().location() ) + src += fmt(" (from %s)", n.meta().location().file()); + + module = n.id(); + unit->addInitialization(cg->compile(n.statements())); + } + + void operator()(const declaration::ImportedModule& n) { + GlobalsVisitor::addDeclarations(cg, *n.module(), n.id(), unit, false); + } + + void operator()(const declaration::LocalVariable& n) { + // Ignore, we'll treat them during statement processing. + } + + void operator()(const declaration::GlobalVariable& n) { + // Ignore, the GlobalsVisitor() handles them. + } + + void operator()(const declaration::Constant& n) { + // Ignore, the GlobalsVisitor() handles them. + } + + void operator()(const declaration::Parameter& n) { + // Ignore, we'll treat it during function processing. + } + + void operator()(const declaration::Function& n, position_t p) { + // TODO(robin): This method needs a refactoring. + + if ( AttributeSet::find(n.function().attributes(), "&cxxname") ) + return; + + auto f = n.function(); + auto ft = f.type(); + auto ns = unit->cxxNamespace(); + auto id = n.id(); + auto linkage = n.linkage(); + auto is_hook = (n.function().type().flavor() == type::function::Flavor::Hook); + + auto id_module = n.id().sub(-3); + + if ( id_module.empty() ) + id_module = *module; + + auto id_class = n.id().sub(-2); + auto id_local = n.id().sub(-1); + auto id_struct_type = (id_module != *module ? ID(id_module, id_class) : id_class); + + cxx::ID cid; + if ( ! is_hook ) + cid = cxx::ID(*module); + + auto d = cg->compile(id, ft, linkage, f.callingConvention(), f.attributes(), cid); + + if ( is_hook && n.linkage() == declaration::Linkage::Struct ) { + // A struct hook. + + if ( ! f.body() ) + // The struct type takes care of the declaration. + return; + + auto id_hook_impl = cxx::ID(unit->cxxNamespace(), fmt("__hook_%s_%s_%p", id_class, id_local, &n)); + auto id_hook_stub = + cxx::ID(cg->options().cxx_namespace_intern, id_module, fmt("__hook_%s_%s", id_class, id_local)); + + // Adapt the function we generate. + d.linkage = "extern"; + d.id = id_hook_impl; + + auto self = lookupID(id_struct_type, p); + assert(self); + + // TODO(robin): This should compile the struct type, not hardcode + // the runtime representation. However, we don't have access to + // the type currently. + d.args.push_back(cxx::declaration::Argument{ + .id = "__self", + .type = fmt("hilti::rt::ValueReference<%s>&", id_struct_type), + }); + + // Make any additional types the hook needs known to local unit and linker. + std::list aux_types{ + cxx::declaration::Type{.id = cxx::ID(cg->options().cxx_namespace_intern, id_module, id_class), + .type = fmt("struct %s", id_class), + .forward_decl = true}}; + + for ( const auto& p : ft.parameters() ) { + if ( auto t = cg->typeDeclaration(p.type()) ) + aux_types.push_back(std::move(*t)); + } + + for ( const auto& t : aux_types ) + cg->unit()->add(t); // XXX + + // Tell linker about our implementation. + auto hook_join = cxx::linker::Join{.id = id_hook_stub, .callee = d, .aux_types = aux_types}; + + cg->unit()->add(d); + cg->unit()->add(hook_join); + } + + if ( is_hook && n.linkage() != declaration::Linkage::Struct ) { + // A function hook. + auto id_module = n.id().sub(-2); + + if ( id_module.empty() ) + id_module = *module; + + auto id_local = id.sub(-1); + auto id_hook_impl = cxx::ID(unit->cxxNamespace(), fmt("__hook_%s_%s_%p", id_module, id_local, &n)); + auto id_hook_stub = cxx::ID(cg->options().cxx_namespace_intern, id_module, id_local); + + // Adapt the function we generate. + d.linkage = "extern"; + d.id = id_hook_impl; + + // Make any additional types the hook needs known to local unit and linker. + std::list aux_types; + + for ( const auto& p : ft.parameters() ) { + if ( auto t = cg->typeDeclaration(p.type()) ) + aux_types.push_back(std::move(*t)); + } + + for ( const auto& t : aux_types ) + cg->unit()->add(t); + + // Tell linker about our implementation. + auto hook_join = cxx::linker::Join{.id = id_hook_stub, + .callee = d, + .aux_types = aux_types, + .declare_only = (! f.body().has_value())}; + + cg->unit()->add(hook_join); + } + + // Common code for all functions, compiling the body. + + if ( ! f.body() ) + return; + + auto body = cg->compile(*f.body()); + + if ( n.linkage() == declaration::Linkage::Struct && ! f.isStatic() ) { + if ( ! is_hook ) { + // TODO(robin): This should compile the struct type, not + // hardcode the runtime representation. However, we do not + // have access to the type currently. + auto self = + cxx::declaration::Local{.id = "__self", + .type = "auto", + .init = fmt("hilti::rt::ValueReference<%s>::self(this)", id_struct_type)}; + body.addStatementAtFront(std::move(self)); + } + + cg->pushSelf("__self.derefAsValue()"); + } + + auto cxx_func = cxx::Function{.declaration = d, .body = std::move(body)}; + + if ( cg->options().debug_flow ) { + std::vector args; + std::vector fmts; + + for ( const auto& p : f.type().parameters() ) { + args.emplace_back(fmt(", %s", cxx::ID(p.id()))); + fmts.emplace_back("%s"); + } + + auto dbg = fmt("HILTI_RT_DEBUG(\"hilti-flow\", hilti::rt::fmt(\"%s: %s(%s)\"%s))", f.meta().location(), + d.id, util::join(fmts, ", "), util::join(args, "")); + + cxx_func.body.addStatementAtFront(std::move(dbg)); + } + + cg->unit()->add(cxx_func); + + if ( f.callingConvention() == function::CallingConvention::Extern ) { + // Create a separate function that we expose to C++. Inside that + // wrapper we execute the actual function inside a lambda + // function prepared to suspend. + auto body = cxx::Block(); + auto cb = cxx::Block(); + auto args = + util::join(util::transform(cxx_func.declaration.args, [](auto& x) { return fmt("%s", x.id); }), ", "); + + cb.addReturn(fmt("%s(%s)", d.id, args)); + auto rt = (ft.result().type() != type::Void() ? " -> std::any" : ""); + body.addLambda("cb", fmt("[&](hilti::rt::resumable::Handle* r)%s", rt), std::move(cb)); + + body.addLocal( + cxx::declaration::Local{.id = "r", .type = "hilti::rt::Resumable", .init = "{std::move(cb)}"}); + body.addStatement("r.run()"); + body.addReturn("r"); + + auto extern_d = d; + extern_d.id = cxx::ID( + util::replace(extern_d.id, cg->options().cxx_namespace_intern, cg->options().cxx_namespace_extern)); + extern_d.result = "hilti::rt::Resumable"; + + auto extern_cxx_func = cxx::Function{.declaration = extern_d, .body = std::move(body)}; + + cg->unit()->add(extern_cxx_func); + cg->unit()->add(extern_d); + } + + if ( n.linkage() == declaration::Linkage::Struct && ! f.isStatic() ) + cg->popSelf(); + + if ( n.linkage() != declaration::Linkage::Struct ) + cg->unit()->add(d); + + if ( n.linkage() == declaration::Linkage::Init ) { + // Add a call to this to the module's initialization code. + cxx::Block call_init_func; + call_init_func.addStatement(fmt("%s()", d.id)); + cg->unit()->addInitialization(call_init_func); + } + } + + void operator()(const declaration::Type& n) { cg->compile(n.type(), codegen::TypeUsage::Storage); } +}; + +} // anonymous namespace + +cxx::Unit* CodeGen::unit() const { + if ( ! _cxx_unit ) + logger().internalError("CodeGen method cannot be used outside of module compilation"); + + return _cxx_unit.get(); +} + +hilti::Unit* CodeGen::hiltiUnit() const { + if ( ! _hilti_unit ) + logger().internalError("CodeGen method cannot be used outside of module compilation"); + + return _hilti_unit; +} + +cxx::declaration::Function CodeGen::compile(const ID& id, type::Function ft, declaration::Linkage linkage, + function::CallingConvention cc, const std::optional& fattrs, + std::optional namespace_) { + auto result_ = [&]() { + auto rt = compile(ft.result().type(), codegen::TypeUsage::FunctionResult); + + switch ( ft.flavor() ) { + case hilti::type::function::Flavor::Hook: + case hilti::type::function::Flavor::Method: + case hilti::type::function::Flavor::Standard: return rt; + default: util::cannot_be_reached(); + } + }; + + auto usage_ = [&](auto k) { + switch ( k ) { + case declaration::parameter::Kind::Copy: return codegen::TypeUsage::CopyParameter; + case declaration::parameter::Kind::In: return codegen::TypeUsage::InParameter; + case declaration::parameter::Kind::InOut: return codegen::TypeUsage::InOutParameter; + case declaration::parameter::Kind::Unknown: logger().internalError("parameter kind not set"); + default: util::cannot_be_reached(); + } + }; + + auto param_ = [&](auto p) { + auto t = compile(p.type(), usage_(p.kind())); + return cxx::declaration::Argument{.id = cxx::ID(p.id()), .type = std::move(t)}; + }; + + auto linkage_ = [&]() { + if ( cc == function::CallingConvention::Extern ) + return "extern"; + + switch ( linkage ) { + case declaration::Linkage::Init: + case declaration::Linkage::Public: return "extern"; + case declaration::Linkage::Private: return "static"; + case declaration::Linkage::Struct: return ""; + default: util::cannot_be_reached(); + } + }; + + auto cxx_id = id; + + if ( linkage == declaration::Linkage::Struct ) { + // For method implementations, check if the ID is fully scoped with + // the module name; if so, remove. + if ( id.sub(0) == _hilti_unit->id() ) + cxx_id = id.sub(1, -1); + } + + auto ns = ID(options().cxx_namespace_intern); + + if ( namespace_ && *namespace_ ) + ns += *namespace_; + else + ns += _hilti_unit->id(); + + return cxx::declaration::Function{.result = result_(), + .id = {ns, cxx_id}, + .args = util::transform(ft.parameters(), param_), + .linkage = linkage_()}; +} + +std::vector CodeGen::compileCallArguments(const std::vector& args, + const std::vector& params) { + auto kinds = util::transform(params, [](auto& x) { return x.kind(); }); + return util::transform(util::zip2(args, kinds), [this](auto& x) { + return compile(x.first, x.second == declaration::parameter::Kind::InOut); + }); +} + +Result CodeGen::compileModule(Node& root, hilti::Unit* hilti_unit) { + util::timing::Collector _("hilti/compiler/codegen"); + + _cxx_unit = std::make_unique(context()); + _hilti_unit = hilti_unit; + auto v = Visitor(this, *root.scope(), _cxx_unit.get()); + + for ( auto i : v.walk(&root) ) + v.dispatch(i); + + GlobalsVisitor::addDeclarations(this, root, ID(std::string(_cxx_unit->moduleID())), _cxx_unit.get(), true); + + auto x = _need_decls; + for ( const auto& t : x ) { + if ( auto dt = typeDeclaration(t) ) + unit()->add(*dt); + } + + cxx::Unit u = *_cxx_unit; + _cxx_unit.reset(); + _hilti_unit = nullptr; + + return std::move(u); +} + +Result CodeGen::linkUnits(const std::vector& mds) { + util::timing::Collector _("hilti/linker"); + + cxx::Linker linker(this); + for ( const auto& md : mds ) + linker.add(md); + + linker.finalize(); + if ( auto u = linker.linkerUnit() ) + return u; + + return result::Error("linking of meta data failed"); +} + +cxx::Expression CodeGen::addTmp(const std::string& prefix, const cxx::Expression& init) { + if ( ! cxxBlock() ) + logger().internalError("codegen: cannot add tmp without an active block"); + + int n = 0; + if ( auto i = _tmp_counters.find(prefix); i != _tmp_counters.end() ) + n = i->second; + + auto tmp = cxx::declaration::Local({.id = cxx::ID(fmt("__%s_%d", prefix, ++n)), .type = "auto", .init = init}); + cxxBlock()->addTmp(tmp); + _tmp_counters[prefix] = n; + return std::string(tmp.id); +} + +cxx::Expression CodeGen::addTmp(const std::string& prefix, const cxx::Type& t) { + if ( ! cxxBlock() ) + logger().internalError("codegen: cannot add tmp without an active block"); + + int n = 0; + if ( auto i = _tmp_counters.find(prefix); i != _tmp_counters.end() ) + n = i->second; + + auto tmp = cxx::declaration::Local({.id = cxx::ID(fmt("__%s_%d", prefix, ++n)), .type = t}); + cxxBlock()->addTmp(tmp); + _tmp_counters[prefix] = n; + return std::string(tmp.id); +} diff --git a/hilti/src/compiler/codegen/coercions.cc b/hilti/src/compiler/codegen/coercions.cc new file mode 100644 index 000000000..87aebc593 --- /dev/null +++ b/hilti/src/compiler/codegen/coercions.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +using namespace hilti::detail; + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + Visitor(CodeGen* cg, const cxx::Expression& expr, const Type& dst) : cg(cg), expr(expr), dst(dst) {} + CodeGen* cg; + const cxx::Expression& expr; + const Type& dst; + + result_t operator()(const type::Bytes& src) { + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::Stream(%s)", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from bytes to %s", dst.typename_())); + } + + result_t operator()(const type::Enum& src) { + if ( auto t = dst.tryAs() ) { + auto id = cg->compile(src, codegen::TypeUsage::Storage); + return fmt("(%s != %s::Undef)", expr, id); + } + + logger().internalError(fmt("codegen: unexpected type coercion from enum to %s", dst.typename_())); + } + + result_t operator()(const type::Error& src) { + if ( auto t = dst.tryAs() ) + return fmt("%s(%s)", cg->compile(dst, codegen::TypeUsage::Storage), expr); + + logger().internalError(fmt("codegen: unexpected type coercion from error to %s", dst.typename_())); + } + + result_t operator()(const type::List& src) { + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::Set(%s)", expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::Vector(%s)", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from lisst to %s", dst.typename_())); + } + + result_t operator()(const type::Optional& src) { + if ( auto t = dst.tryAs() ) + return fmt("%s.has_value()", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from optional to %s", dst.typename_())); + } + + result_t operator()(const type::StrongReference& src) { + if ( auto t = dst.tryAs() ) + return fmt("static_cast(%s)", expr); + + if ( auto t = dst.tryAs() ) + return fmt("%s.derefAsValue()", expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::WeakReference<%s>(%s)", + cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); + + if ( src.dereferencedType() == dst ) + return fmt("(*%s)", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from %s to %s", Type(src), dst.typename_())); + } + + result_t operator()(const type::Result& src) { + if ( auto t = dst.tryAs() ) + return fmt("static_cast(%s)", expr); + + if ( auto t = dst.tryAs() ) + return fmt("static_cast<%s>(%s)", cg->compile(dst, codegen::TypeUsage::Storage), expr); + + logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + } + + result_t operator()(const type::SignedInteger& src) { + if ( dst.isA() ) + return fmt("static_cast(%s)", expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + + logger().internalError(fmt("codegen: unexpected type coercion from signed integer to %s", dst.typename_())); + } + + result_t operator()(const type::Stream& src) { + if ( auto t = dst.tryAs() ) + return fmt("%s.view()", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from stream to %s", dst.typename_())); + } + + result_t operator()(const type::Union& src) { + if ( auto t = dst.tryAs() ) { + auto id = cg->compile(src, codegen::TypeUsage::Storage); + return fmt("(%s.index() > 0)", expr); + } + + logger().internalError(fmt("codegen: unexpected type coercion from union to %s", dst.typename_())); + } + + result_t operator()(const type::stream::View& src) { + if ( auto t = dst.tryAs() ) + return fmt("%s.data()", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from view to %s", dst.typename_())); + } + + result_t operator()(const type::Tuple& src) { + if ( auto t = dst.tryAs() ) { + int i = 0; + std::vector exprs; + + for ( auto [src, dst] : util::zip2(src.types(), t->types()) ) + exprs.push_back(cg->coerce(fmt("std::get<%d>(%s)", i++, expr), src, dst)); + + return fmt("std::make_tuple(%s)", util::join(exprs, ", ")); + } + + logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + } + + result_t operator()(const type::UnsignedInteger& src) { + if ( dst.isA() ) + return fmt("static_cast(%s)", expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + + logger().internalError(fmt("codegen: unexpected type coercion from unsigned integer to %s", dst.typename_())); + } + + result_t operator()(const type::WeakReference& src) { + if ( auto t = dst.tryAs() ) + return fmt("static_cast(%s)", expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::StrongReference<%s>(%s)", + cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); + + if ( auto t = dst.tryAs() ) + return fmt("%s.derefAsValue()", expr); + + if ( src.dereferencedType() == dst ) + return fmt("(*%s)", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + } + + result_t operator()(const type::ValueReference& src) { + if ( auto t = dst.tryAs() ) + return cg->coerce(fmt("*%s", expr), src.dereferencedType(), dst); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::StrongReference<%s>(%s)", + cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); + + if ( auto t = dst.tryAs() ) + return fmt("hilti::rt::WeakReference<%s>(%s)", + cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); + + if ( src.dereferencedType() == dst ) + return fmt("(*%s)", expr); + + logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + } +}; + +} // anonymous namespace + +cxx::Expression CodeGen::coerce(const cxx::Expression& e, const Type& src, const Type& dst) { + if ( type::sameExceptForConstness(src, dst) ) + // If only difference is constness, nothing to do. + return e; + + if ( auto t = dst.tryAs(); t && ! src.isA() ) + return fmt("%s(%s)", compile(dst, codegen::TypeUsage::Storage), e); + + if ( auto t = dst.tryAs() ) + return fmt("%s(%s)", compile(dst, codegen::TypeUsage::Storage), e); + + if ( dst.tryAs() && ! type::isReferenceType(src) ) + return e; + + if ( auto nt = Visitor(this, e, dst).dispatch(src) ) + return *nt; + + logger().internalError(fmt("codegen: type %s unhandled for coercion", src.typename_())); +} diff --git a/hilti/src/compiler/codegen/ctors.cc b/hilti/src/compiler/codegen/ctors.cc new file mode 100644 index 000000000..96fe95317 --- /dev/null +++ b/hilti/src/compiler/codegen/ctors.cc @@ -0,0 +1,205 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using namespace hilti::detail; + +using hilti::rt::fmt; + +namespace { + +struct Visitor : hilti::visitor::PreOrder { + explicit Visitor(CodeGen* cg) : cg(cg) {} + CodeGen* cg; + + result_t operator()(const ctor::Address& n) { return fmt("hilti::rt::Address(\"%s\")", n.value()); } + + result_t operator()(const ctor::Bool& n) { return n.value() ? "true" : "false"; } + + result_t operator()(const ctor::Bytes& n) { return fmt("\"%s\"_b", util::escapeBytes(n.value(), true)); } + + result_t operator()(const ctor::Coerced& n) { return cg->compile(n.coercedCtor()); } + + result_t operator()(const ctor::Default& n) { + auto args = util::join(util::transform(n.typeArguments(), [&](const auto e) { return cg->compile(e); }), ", "); + return fmt("(%s(%s))", cg->compile(n.type(), codegen::TypeUsage::Ctor), args); + } + + result_t operator()(const ctor::Error& n) { return fmt("hilti::rt::result::Error(\"%s\")", n.value()); } + + result_t operator()(const ctor::Exception& n) { + std::string type; + + if ( auto x = n.type().cxxID() ) + type = x->str(); + else + type = cg->compile(n.type(), codegen::TypeUsage::Ctor); + + return fmt("%s(%s, \"%s\")", type, cg->compile(n.value()), n.meta().location()); + } + + result_t operator()(const ctor::Interval& n) { + return fmt("hilti::rt::Interval(static_cast(%" PRId64 "))", n.value().nanoseconds()); + } + + result_t operator()(const ctor::List& n) { + if ( n.elementType() == type::unknown ) + // Can only be the empty list. + return "hilti::rt::list::Empty()"; + + return fmt("hilti::rt::List<%s>{{%s}}", cg->compile(n.elementType(), codegen::TypeUsage::Storage), + util::join(util::transform(n.value(), [this](auto e) { return cg->compile(e); }), ", ")); + } + + result_t operator()(const ctor::Map& n) { + if ( n.elementType() == type::unknown ) + // Can only be the empty map. + return "hilti::rt::map::Empty()"; + + auto k = cg->compile(n.keyType(), codegen::TypeUsage::Storage); + auto v = cg->compile(n.elementType(), codegen::TypeUsage::Storage); + + return fmt("hilti::rt::Map<%s, %s>{{%s}}", k, v, + util::join(util::transform(n.value(), + [this](auto e) { + return fmt("{%s, %s}", cg->compile(e.first), cg->compile(e.second)); + }), + ", ")); + } + + result_t operator()(const ctor::Network& n) { + return fmt("hilti::rt::Network(\"%s\", %u)", n.value().prefix(), n.value().length()); + } + + result_t operator()(const ctor::Null& n) { return fmt("hilti::rt::Null()"); } + + result_t operator()(const ctor::Optional& n) { + if ( auto e = n.value() ) + return fmt("std::make_optional(%s)", cg->compile(*e)); + + return "{}"; + } + + result_t operator()(const ctor::Port& n) { return fmt("hilti::rt::Port(\"%s\")", n.value()); } + + result_t operator()(const ctor::Real& n) { + // hexfloat format for lossless output, at most 13 fraction hexits + return fmt("%a", n.value()); + } + + result_t operator()(const ctor::Result& n) { + auto t = cg->compile(n.type(), codegen::TypeUsage::Storage); + + if ( auto e = n.value() ) + return fmt("%s(%s)", t, cg->compile(*e)); + + return fmt("%s(%s)", t, cg->compile(*n.error())); + } + + result_t operator()(const ctor::StrongReference& n) { + return fmt("hilti::rt::StrongReference<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); + } + + result_t operator()(const ctor::RegExp& n) { + std::vector flags; + + if ( n.isNoSub() ) + flags.emplace_back(".no_sub = 1"); + + auto t = (n.value().size() == 1 ? "std::string" : "std::vector"); + return fmt("hilti::rt::RegExp(%s{%s}, {%s})", t, + util::join(util::transform(n.value(), + [&](auto s) { return fmt("\"%s\"", util::escapeUTF8(s, true, false)); }), + ", "), + util::join(flags, ", ")); + } + + result_t operator()(const ctor::Set& n) { + if ( n.elementType() == type::unknown ) + // Can only be the empty list. + return "hilti::rt::set::Empty()"; + + return fmt("hilti::rt::Set<%s>{{%s}}", cg->compile(n.elementType(), codegen::TypeUsage::Storage), + util::join(util::transform(n.value(), [this](auto e) { return fmt("%s", cg->compile(e)); }), ", ")); + } + + result_t operator()(const ctor::SignedInteger& n) { + if ( /* n.width() == 64 && */ n.value() == INT64_MIN ) + return fmt("hilti::rt::integer::safe{INT64_MIN}"); + + return fmt("hilti::rt::integer::safe{%" PRId64 "}", n.width(), n.value()); + } + + result_t operator()(const ctor::Stream& n) { + return fmt("hilti::rt::Stream(\"%s\"_b)", util::escapeBytes(n.value(), true)); + } + + result_t operator()(const ctor::String& n) { return fmt("std::string(\"%s\")", util::escapeUTF8(n.value(), true)); } + + result_t operator()(const ctor::Tuple& n) { + return fmt("std::make_tuple(%s)", + util::join(util::transform(n.value(), [this](auto e) { return cg->compile(e); }), ", ")); + } + + result_t operator()(const ctor::Struct& n) { + auto id = cg->compile(n.type(), codegen::TypeUsage::Ctor); + + auto is_field = [&](auto f) { return ! f.type().template isA(); }; + + auto convert_field = [&](auto f) { + if ( const auto& c = n.field(f.id()) ) + return cg->compile(c->second); + + return cxx::Expression("{}"); + }; + + return fmt("%s(%s)", id, + util::join(util::transform(util::filter(n.type().as().fields(), is_field), + convert_field), + ", ")); + } + + result_t operator()(const ctor::Time& n) { return fmt("hilti::rt::Time(%f)", n.value().seconds()); } + + result_t operator()(const ctor::Enum& n) { + auto id = cg->compile(n.type(), codegen::TypeUsage::Storage); + return fmt("%s::%s", id, n.value().id()); + } + + result_t operator()(const ctor::ValueReference& n) { + return fmt("hilti::rt::reference::make_value<%s>(%s)", + cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor), cg->compile(n.expression())); + } + + result_t operator()(const ctor::Vector& n) { + if ( n.elementType() == type::unknown ) + // Can only be the empty list. + return "hilti::rt::vector::Empty()"; + + return fmt("hilti::rt::Vector<%s>{{%s}}", cg->compile(n.elementType(), codegen::TypeUsage::Storage), + util::join(util::transform(n.value(), [this](auto e) { return fmt("%s", cg->compile(e)); }), ", ")); + } + + result_t operator()(const ctor::UnsignedInteger& n) { + return fmt("hilti::rt::integer::safe{%" PRId64 "u}", n.width(), n.value()); + } + + result_t operator()(const ctor::WeakReference& n) { + return fmt("hilti::rt::WeakReference<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); + } +}; + +} // anonymous namespace + +cxx::Expression CodeGen::compile(const hilti::Ctor& c) { + if ( auto x = Visitor(this).dispatch(c) ) + return cxx::Expression(*x); + + logger().internalError(fmt("ctor %s failed to compile", to_node(c).typename_()), c); +} diff --git a/hilti/src/compiler/codegen/expressions.cc b/hilti/src/compiler/codegen/expressions.cc new file mode 100644 index 000000000..d1e0ec1b1 --- /dev/null +++ b/hilti/src/compiler/codegen/expressions.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +using namespace hilti::detail; + +namespace { + +struct Visitor : hilti::visitor::PreOrder { + Visitor(CodeGen* cg, bool lhs) : cg(cg), lhs(lhs) {} + CodeGen* cg; + bool lhs; + + result_t operator()(const expression::Assign& n) { + if ( auto c = n.target().tryAs() ) { + if ( c->ctor().type().isA() ) { + auto t = c->ctor().as().value(); + auto l = util::join(util::transform(t, [this](auto& x) { return cg->compile(x, true); }), ", "); + return fmt("std::tie(%s) = %s", l, cg->compile(n.source())); + } + } + + return fmt("%s = %s", cg->compile(n.target(), true), cg->compile(n.source())); + } + + result_t operator()(const expression::Coerced& n) { + return cg->coerce(cg->compile(n.expression(), lhs), n.expression().type(), n.type()); + } + + result_t operator()(const expression::Ctor& n) { + auto e = cg->compile(n.ctor()); + + if ( ! lhs ) + return std::move(e); + + return cg->addTmp("ctor", e); + } + + result_t operator()(const expression::Deferred& n) { + auto type = cg->compile(n.type(), codegen::TypeUsage::Storage); + auto value = cg->compile(n.expression()); + + if ( n.catchException() ) + return fmt( + "hilti::rt::DeferredExpression<%s>([=]() -> %s { try { return %s; } catch ( ... ) { return " + "hilti::rt::result::Error(\"n/a\"); } })", + type, type, value); + else + return fmt("hilti::rt::DeferredExpression<%s>([=]() -> %s { return %s; })", type, type, value); + } + + result_t operator()(const expression::Keyword& n) { + switch ( n.kind() ) { + case expression::keyword::Kind::Self: return cg->self(); + case expression::keyword::Kind::DollarDollar: return cg->dollardollar(); + default: util::cannot_be_reached(); + } + } + + result_t operator()(const expression::ListComprehension& n) { + auto id = cxx::ID(n.id()); + auto input = cg->compile(n.input()); + auto itype = cg->compile(n.input().type().elementType(), codegen::TypeUsage::Storage); + auto otype = cg->compile(n.output().type(), codegen::TypeUsage::Storage); + auto output = cg->compile(n.output()); + auto pred = std::string(); + + if ( auto c = n.condition() ) + pred = fmt(", [](auto&& %s) -> bool { return %s; }", id, cg->compile(*c)); + + return fmt("hilti::rt::list::make<%s, %s>(%s, [](auto&& %s) -> %s { return %s; }%s)", itype, otype, input, id, + otype, output, pred); + } + + result_t operator()(const expression::Member& n) { + logger().internalError(fmt("expression::Member should never be evaluated ('%s')", n), n); + } + + result_t operator()(const expression::Move& n) { + if ( ! lhs ) + return fmt("std::move(%s)", cg->compile(n.expression())); + + return cg->compile(n.expression(), true); + } + + result_t operator()(const expression::LogicalAnd& n) { + return fmt("(%s) && (%s)", cg->compile(n.op0()), cg->compile(n.op1())); + } + + result_t operator()(const expression::LogicalNot& n) { return fmt("! (%s)", cg->compile(n.expression())); } + + result_t operator()(const expression::LogicalOr& n) { + return fmt("(%s) || (%s)", cg->compile(n.op0()), cg->compile(n.op1())); + } + + result_t operator()(const expression::ResolvedID& n, position_t p) { + if ( auto g = n.declaration().tryAs() ) { + if ( auto ns = n.id().namespace_(); ! ns.empty() ) + return fmt("%s->%s", cxx::ID(ns, "__globals()"), cxx::ID(n.id().local())); + + return fmt("__globals()->%s", cxx::ID(n.id())); + } + + if ( auto e = n.declaration().tryAs() ) + return cg->compile(e->expression(), lhs); + + if ( auto c = n.declaration().tryAs() ) { + if ( c->value().type().isA() ) + return cg->compile(c->value()); // This constructs the right ID. + + return cxx::ID(cg->options().cxx_namespace_intern, cxx::ID(n.id())); + } + + if ( auto f = n.declaration().tryAs() ) { + // If we're refering to, but not calling, an "external" function + // or static method, bind to the externally visible name. + if ( f->function().callingConvention() == function::CallingConvention::Extern && + (p.path.empty() || ! p.parent().isA()) ) + return cxx::ID(cg->options().cxx_namespace_extern, cxx::ID(n.id())); + } + + if ( auto p = n.declaration().tryAs(); p && p->isStructParameter() ) { + // Need to adjust here for potential automatic change to a weak reference. + if ( type::isReferenceType(p->type()) ) + return cxx::Expression(fmt("__self->__p_%s.derefAsValue()", p->id())); + else + return cxx::Expression(fmt("__self->__p_%s", p->id())); + } + + return cxx::ID(n.id()); + } + + result_t operator()(const expression::ResolvedOperator& n) { return cg->compile(n, lhs); } + + result_t operator()(const expression::Ternary& n) { + return fmt("(%s ? %s : %s)", cg->compile(n.condition()), cg->compile(n.true_()), cg->compile(n.false_())); + } + + result_t operator()(const expression::TypeWrapped& n) { return cg->compile(n.expression(), lhs); } + + result_t operator()(const expression::UnresolvedID& n, position_t p) { + hilti::print(std::cerr, p.node); + hilti::render(std::cerr, p.node); + logger().internalError("unresolved expression ID", n); + } + + result_t operator()(const expression::UnresolvedOperator& n, position_t p) { + hilti::print(std::cerr, p.node); + hilti::render(std::cerr, p.node); + logger().internalError("unresolved operator", n); + } + + result_t operator()(const expression::Void& n) { return ""; } +}; + +} // anonymous namespace + +cxx::Expression CodeGen::compile(const hilti::Expression& e, bool lhs) { + if ( auto x = Visitor(this, lhs).dispatch(e) ) + return *x; + + logger().internalError(fmt("expression failed to compile ('%s' / %s)", e, e.typename_()), e); +} diff --git a/hilti/src/compiler/codegen/operators.cc b/hilti/src/compiler/codegen/operators.cc new file mode 100644 index 000000000..3b5838aa2 --- /dev/null +++ b/hilti/src/compiler/codegen/operators.cc @@ -0,0 +1,928 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +using namespace hilti::detail; + +namespace { + +struct Visitor : hilti::visitor::PreOrder { + Visitor(CodeGen* cg, bool lhs) : cg(cg), lhs(lhs) {} + CodeGen* cg; + bool lhs; + + // Helpers + + result_t op0(const expression::ResolvedOperatorBase& o, bool lhs = false) { return cg->compile(o.op0(), lhs); } + + result_t op1(const expression::ResolvedOperatorBase& o, bool lhs = false) { return cg->compile(o.op1(), lhs); } + + result_t op2(const expression::ResolvedOperatorBase& o, bool lhs = false) { return cg->compile(o.op2(), lhs); } + + result_t binary(const expression::ResolvedOperatorBase& o, const std::string& x) { + return fmt("%s %s %s", op0(o), x, op1(o)); + } + + auto compileExpressions(const std::vector& exprs) { + return util::transform(exprs, [&](const auto& e) { return cg->compile(e); }); + } + + auto tupleArguments(const expression::ResolvedOperatorBase& o, const Expression& op) { + auto ctor = op.as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return compileExpressions(ctor.as().value()); + } + + auto methodArguments(const expression::ResolvedOperatorBase& o) { + auto ctor = o.op2().as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return std::make_pair(op0(o), compileExpressions(ctor.as().value())); + } + + auto optionalArgument(const std::vector& args, unsigned int i) { + return i < args.size() ? std::string(args[i]) : ""; + } + + std::optional optionalArgument(const std::vector& args, unsigned int i) { + if ( i < args.size() ) + return cg->compile(args[i], false); + + return {}; + } + + /// Address + + result_t operator()(const operator_::address::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::address::Unequal& n) { return binary(n, "!="); } + result_t operator()(const operator_::address::Family& n) { return fmt("%s.family()", op0(n)); } + + /// Bool + + result_t operator()(const operator_::bool_::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::bool_::Unequal& n) { return binary(n, "!="); } + + /// bytes::Iterator + + result_t operator()(const operator_::bytes::iterator::Deref& n) { return fmt("*%s", op0(n)); } + result_t operator()(const operator_::bytes::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::bytes::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::bytes::iterator::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::Lower& n) { return fmt("%s < %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::LowerEqual& n) { return fmt("%s <= %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::Greater& n) { return fmt("%s > %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::GreaterEqual& n) { return fmt("%s >= %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::Difference& n) { return fmt("%s - %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::SumAssign& n) { return fmt("%s += %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::iterator::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + // Bytes + + result_t operator()(const operator_::bytes::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::bytes::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::Lower& n) { return fmt("%s < %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::LowerEqual& n) { return fmt("%s <= %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::Greater& n) { return fmt("%s > %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::GreaterEqual& n) { return fmt("%s >= %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::SumAssignBytes& n) { return fmt("%s.append(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::SumAssignStreamView& n) { return fmt("%s.append(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::bytes::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::bytes::In& n) { return fmt("std::get<0>(%s.find(%s))", op1(n), op0(n)); } + + result_t operator()(const operator_::bytes::Find& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.find(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::LowerCase& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.lower(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::UpperCase& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.upper(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::At& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.at(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::Split& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.split(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::Split1& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.split1(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::StartsWith& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.startsWith(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::Strip& n) { + auto [self, args] = methodArguments(n); + + std::string x; + + if ( auto side = optionalArgument(args, 1); ! side.empty() ) + x = side; + + if ( auto set = optionalArgument(args, 0); ! set.empty() ) { + if ( x.size() ) + x += ", "; + + x += set; + } + + return fmt("%s.strip(%s)", self, x); + } + + result_t operator()(const operator_::bytes::SubIterators& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.sub(%s, %s)", self, args[0], args[1]); + } + + result_t operator()(const operator_::bytes::SubIterator& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.sub(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::SubOffsets& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.sub(%s, %s)", self, args[0], args[1]); + } + + result_t operator()(const operator_::bytes::Join& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.join(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::ToIntAscii& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.toInt(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::ToUIntAscii& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.toUInt(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::ToIntBinary& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.toInt(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::ToUIntBinary& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.toUInt(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::ToTimeAscii& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.toTime(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::ToTimeBinary& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.toTime(%s)", self, optionalArgument(args, 0)); + } + + result_t operator()(const operator_::bytes::Decode& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.decode(%s)", self, args[0]); + } + + result_t operator()(const operator_::bytes::Match& n) { + auto [self, args] = methodArguments(n); + + std::string group; + + if ( auto x = optionalArgument(args, 1); ! x.empty() ) + group = fmt(", %s", x); + + return fmt("%s.match(%s%s)", self, args[0], group); + } + + // Enum + + result_t operator()(const operator_::enum_::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::enum_::Unequal& n) { return binary(n, "!="); } + + result_t operator()(const operator_::enum_::CastFromUnsignedInteger& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s.Ref())", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::enum_::CastToSignedInteger& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::enum_::CastToUnsignedInteger& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::enum_::Ctor& n) { + auto args = tupleArguments(n, n.op1()); + auto t = n.op0().type().as().typeValue(); + return fmt("static_cast<%s>(%s.Ref())", cg->compile(t, codegen::TypeUsage::Storage), args[0]); + } + + // Exception + result_t operator()(const operator_::exception::Ctor& n) { + std::string type; + + auto args = tupleArguments(n, n.op1()); + + if ( auto x = n.op0().type().cxxID() ) + type = x->str(); + else + type = cg->compile(n.op0().type().as().typeValue(), codegen::TypeUsage::Ctor); + + return fmt("%s(%s, \"%s\")", type, args[0], n.meta().location()); + } + + result_t operator()(const operator_::exception::Description& n) { return fmt("%s.description()", op0(n)); } + + // Interval + + result_t operator()(const operator_::interval::CastFromReal& n) { return fmt("hilti::rt::Interval(%f)", op0(n)); } + result_t operator()(const operator_::interval::CastFromSignedInteger& n) { + return fmt("hilti::rt::Interval(hilti::rt::integer::safe(%" PRId64 ") * 1000000000)", op0(n)); + } + result_t operator()(const operator_::interval::CastFromUnsignedInteger& n) { + return fmt("hilti::rt::Interval(hilti::rt::integer::safe(%" PRIu64 ") * 1000000000)", op0(n)); + } + result_t operator()(const operator_::interval::Difference& n) { return binary(n, "-"); } + result_t operator()(const operator_::interval::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::interval::Greater& n) { return binary(n, ">"); } + result_t operator()(const operator_::interval::GreaterEqual& n) { return binary(n, ">="); } + result_t operator()(const operator_::interval::Lower& n) { return binary(n, "<"); } + result_t operator()(const operator_::interval::LowerEqual& n) { return binary(n, "<="); } + result_t operator()(const operator_::interval::MultipleUnsignedInteger& n) { return binary(n, "*"); } + result_t operator()(const operator_::interval::MultipleReal& n) { return binary(n, "*"); } + result_t operator()(const operator_::interval::Nanoseconds& n) { return fmt("%s.nanoseconds()", op0(n)); } + result_t operator()(const operator_::interval::Seconds& n) { return fmt("%s.seconds()", op0(n)); } + result_t operator()(const operator_::interval::Sum& n) { return binary(n, "+"); } + result_t operator()(const operator_::interval::Unequal& n) { return binary(n, "!="); } + + // List + result_t operator()(const operator_::list::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::list::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::list::iterator::Deref& n) { return fmt("*%s", op0(n)); } + result_t operator()(const operator_::list::iterator::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::list::iterator::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::list::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::list::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::list::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + // Map + + result_t operator()(const operator_::map::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::map::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::map::iterator::Deref& n) { return fmt("*%s", op0(n)); } + result_t operator()(const operator_::map::iterator::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::map::iterator::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::map::Delete& n) { return fmt("%s.erase(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::map::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::map::In& n) { return fmt("%s.contains(%s)", op1(n), op0(n)); } + result_t operator()(const operator_::map::IndexConst& n) { return fmt("%s[%s]", op0(n), op1(n)); } + result_t operator()(const operator_::map::IndexNonConst& n) { return fmt("%s[%s]", op0(n), op1(n)); } + result_t operator()(const operator_::map::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::map::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::map::Get& n) { + auto [self, args] = methodArguments(n); + + std::string x = args[0]; + ; + + if ( auto default_ = optionalArgument(args, 1); ! default_.empty() ) { + x += ", "; + x += default_; + } + + return fmt("%s.get(%s)", self, x); + } + + result_t operator()(const operator_::map::Clear& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.clear()", self); + } + + /// Network + + result_t operator()(const operator_::network::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::network::Unequal& n) { return binary(n, "!="); } + result_t operator()(const operator_::network::Family& n) { return fmt("%s.family()", op0(n)); } + result_t operator()(const operator_::network::Prefix& n) { return fmt("%s.prefix()", op0(n)); } + result_t operator()(const operator_::network::Length& n) { return fmt("%s.length()", op0(n)); } + result_t operator()(const operator_::network::In& n) { return fmt("%s.contains(%s)", op1(n), op0(n)); } + + /// Real + + result_t operator()(const operator_::real::SignNeg& n) { return fmt("(-%s)", op0(n)); } + result_t operator()(const operator_::real::Difference& n) { return binary(n, "-"); } + result_t operator()(const operator_::real::DifferenceAssign& n) { return binary(n, "-="); } + result_t operator()(const operator_::real::Division& n) { return binary(n, "/"); } + result_t operator()(const operator_::real::DivisionAssign& n) { return binary(n, "/="); } + result_t operator()(const operator_::real::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::real::Greater& n) { return binary(n, ">"); } + result_t operator()(const operator_::real::GreaterEqual& n) { return binary(n, ">="); } + result_t operator()(const operator_::real::Lower& n) { return binary(n, "<"); } + result_t operator()(const operator_::real::LowerEqual& n) { return binary(n, "<="); } + result_t operator()(const operator_::real::Modulo& n) { return fmt("std::fmod(%s,%s)", op0(n), op1(n)); } + result_t operator()(const operator_::real::Multiple& n) { return binary(n, "*"); } + result_t operator()(const operator_::real::MultipleAssign& n) { return binary(n, "*="); } + result_t operator()(const operator_::real::Power& n) { return fmt("std::pow(%s, %s)", op0(n), op1(n)); } + result_t operator()(const operator_::real::Sum& n) { return binary(n, "+"); } + result_t operator()(const operator_::real::SumAssign& n) { return binary(n, "+="); } + result_t operator()(const operator_::real::Unequal& n) { return binary(n, "!="); } + + result_t operator()(const operator_::real::CastSignedInteger& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::real::CastUnsignedInteger& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + /// Result + result_t operator()(const operator_::error::Description& n) { return fmt("%s.description()", op0(n)); } + + result_t operator()(const operator_::result::Deref& n) { return fmt("%s.valueOrThrow()", op0(n)); } + + result_t operator()(const operator_::result::Error& n) { return fmt("%s.errorOrThrow()", op0(n)); } + + result_t operator()(const operator_::generic::Unpack& n) { + auto args = tupleArguments(n, n.op1()); + return cg->unpack(n.op0().type().as().typeValue(), args[0], util::slice(args, 1, -1)); + } + + result_t operator()(const operator_::generic::Begin& n) { return fmt("hilti::rt::safe_begin(%s)", op0(n)); } + + result_t operator()(const operator_::generic::End& n) { return fmt("hilti::rt::safe_end(%s)", op0(n)); } + + result_t operator()(const operator_::generic::New& n) { + if ( auto tv = n.op0().type().tryAs() ) { + auto args = util::join(tupleArguments(n, n.op1()), ", "); + return fmt("hilti::rt::reference::make_strong<%s>(%s)", + cg->compile(tv->typeValue(), codegen::TypeUsage::Ctor), args); + } + else { + return fmt("hilti::rt::reference::make_strong<%s>(%s)", + cg->compile(n.op0().type(), codegen::TypeUsage::Ctor), op0(n)); + } + } + + result_t operator()(const operator_::generic::CastedCoercion& n) { + return cg->compile(expression::Coerced(n.op0(), n.result(), n.meta())); + } + + result_t operator()(const operator_::function::Call& n) { + // 1st operand directly references a function, validator ensures that. + auto f = n.op0().as().declaration().as(); + + auto name = op0(n); + + if ( auto a = AttributeSet::find(f.function().attributes(), "&cxxname") ) { + auto s = a->valueAs(); + if ( s ) + name = *s; + else + logger().error(s, n); + } + + auto values = n.op1().as().ctor().as().value(); + return fmt("%s(%s)", name, + util::join(cg->compileCallArguments(values, f.function().type().parameters()), ", ")); + } + + result_t operator()(const operator_::regexp::Find& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.find(%s)", self, args[0]); + } + + result_t operator()(const operator_::regexp::FindGroups& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.findGroups(%s)", self, args[0]); + } + + result_t operator()(const operator_::regexp::FindSpan& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.findSpan(%s)", self, args[0]); + } + + result_t operator()(const operator_::regexp::TokenMatcher& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.tokenMatcher()", self); + } + + result_t operator()(const operator_::regexp_match_state::AdvanceBytes& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.advance(%s, %s)", self, args[0], args[1]); + } + + result_t operator()(const operator_::regexp_match_state::AdvanceView& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.advance(%s)", self, args[0]); + } + + // Optional + result_t operator()(const operator_::optional::Deref& n) { + return fmt("hilti::rt::optional::value(%s, \"%s\")", op0(n), n.op0().meta().location().render()); + } + + /// Port + + result_t operator()(const operator_::port::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::port::Unequal& n) { return binary(n, "!="); } + result_t operator()(const operator_::port::Protocol& n) { return fmt("%s.protocol()", op0(n)); } + + // Set + result_t operator()(const operator_::set::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::set::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::set::iterator::Deref& n) { return fmt("*%s", op0(n)); } + result_t operator()(const operator_::set::iterator::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::set::iterator::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::set::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::set::In& n) { return fmt("%s.contains(%s)", op1(n), op0(n)); } + result_t operator()(const operator_::set::Add& n) { return fmt("%s.insert(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::set::Delete& n) { return fmt("%s.erase(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::set::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::set::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::set::Clear& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.clear()", self); + } + + /// stream::Iterator + + result_t operator()(const operator_::stream::iterator::Deref& n) { return fmt("*%s", op0(n)); } + result_t operator()(const operator_::stream::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::stream::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::stream::iterator::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::Lower& n) { return fmt("%s < %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::LowerEqual& n) { return fmt("%s <= %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::Greater& n) { return fmt("%s > %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::GreaterEqual& n) { return fmt("%s >= %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::Difference& n) { return fmt("%s - %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::SumAssign& n) { return fmt("%s += %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::iterator::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::stream::iterator::Offset& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.offset()", self); + } + + result_t operator()(const operator_::stream::iterator::IsFrozen& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.isFrozen()", self); + } + + /// stream::View + + result_t operator()(const operator_::stream::view::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::stream::view::EqualView& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::view::EqualBytes& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::view::UnequalView& n) { return fmt("%s != %s", op0(n), op1(n)); } + result_t operator()(const operator_::stream::view::UnequalBytes& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::stream::view::Offset& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.offset()", self); + } + + result_t operator()(const operator_::stream::view::InBytes& n) { + return fmt("std::get<0>(%s.find(%s))", op1(n), op0(n)); + } + result_t operator()(const operator_::stream::view::InView& n) { + return fmt("std::get<0>(%s.find(%s))", op1(n), op0(n)); + } + + result_t operator()(const operator_::stream::view::AdvanceTo& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.advance(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::view::AdvanceBy& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.advance(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::view::Limit& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.limit(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::view::Find& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.find(%s)", self, args[0]); + } + + + result_t operator()(const operator_::stream::view::At& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.at(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::view::StartsWith& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.startsWith(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::view::SubIterators& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.sub(%s, %s)", self, args[0], args[1]); + } + + result_t operator()(const operator_::stream::view::SubIterator& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.sub(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::view::SubOffsets& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.sub(%s, %s)", self, args[0], args[1]); + } + + + // Stream + + result_t operator()(const operator_::stream::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::stream::SumAssignView& n) { return fmt("%s.append(%s)", op0(n), op1(n)); } + result_t operator()(const operator_::stream::SumAssignBytes& n) { return fmt("%s.append(%s)", op0(n), op1(n)); } + + result_t operator()(const operator_::stream::Freeze& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.freeze()", self); + } + + result_t operator()(const operator_::stream::Unfreeze& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.unfreeze()", self); + } + + result_t operator()(const operator_::stream::IsFrozen& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.isFrozen()", self); + } + + result_t operator()(const operator_::stream::At& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.at(%s)", self, args[0]); + } + + result_t operator()(const operator_::stream::Trim& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.trim(%s)", self, args[0]); + } + + // String + + result_t operator()(const operator_::string::Sum& n) { return binary(n, "+"); } + result_t operator()(const operator_::string::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::string::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::string::Unequal& n) { return binary(n, "!="); } + result_t operator()(const operator_::string::Modulo& n) { + if ( n.op1().type().isA() ) { + if ( auto ctor = n.op1().tryAs() ) { + auto t = ctor->ctor().as().value(); + return fmt("hilti::rt::fmt(%s, %s)", op0(n), + util::join(util::transform(t, [this](auto& x) { return cg->compile(x); }), ", ")); + } + } + + return fmt("hilti::rt::fmt(%s, %s)", op0(n), op1(n)); + } + + // Strong reference + result_t operator()(const operator_::strong_reference::Deref& n) { return fmt("(*%s)", op0(n)); } + result_t operator()(const operator_::strong_reference::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::strong_reference::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + /// Struct + + auto memberAccess(const expression::ResolvedOperatorBase& o, const std::string& self, const std::string& member) { + return fmt("%s.%s", self, cxx::ID(member)); + } + + auto memberAccess(const expression::ResolvedOperatorBase& o, const std::string& member, bool lhs = false) { + return memberAccess(o, cg->compile(o.op0(), lhs), member); + } + + result_t structMember(const expression::ResolvedOperatorBase& o, const Expression& op1) { + auto op0 = o.op0(); + auto id = op1.as().id(); + auto attr = memberAccess(o, id); + + if ( auto f = op0.type().as().field(id); f->isOptional() ) { + auto d = f->default_(); + + if ( lhs ) { + if ( d ) + return fmt("hilti::rt::optional::valueOrInit(%s, %s)", attr, cg->compile(*d)); + + return fmt("hilti::rt::optional::valueOrInit(%s)", attr); + } + + if ( d ) + return fmt("%s.value_or(%s)", attr, cg->compile(*d)); + + return fmt("hilti::rt::optional::value(%s, \"%s\")", attr, op0.meta().location().render()); + } + + return attr; + } + + result_t operator()(const operator_::struct_::MemberConst& n) { return structMember(n, n.op1()); } + result_t operator()(const operator_::struct_::MemberNonConst& n) { return structMember(n, n.op1()); } + + result_t operator()(const operator_::struct_::MemberCall& n) { + auto id = n.op1().as().id(); + auto ft = n.op1().as().type().as(); + auto args = n.op2().as().ctor().as().value(); + auto kinds = util::transform(ft.parameters(), [](auto& x) { return x.kind(); }); + auto zipped = util::zip2(args, kinds); + return memberAccess(n, + fmt("%s(%s)", id, + util::join(util::transform(zipped, + [this](auto& x) { + return cg + ->compile(x.first, + x.second == + declaration::parameter::Kind::InOut); + }), + ", ")), + false); + } + + result_t operator()(const operator_::struct_::HasMember& n) { + auto id = n.op1().as().id(); + + if ( auto f = n.op0().type().as().field(id); f->isOptional() ) + return fmt("%s.has_value()", memberAccess(n, id)); + + return "true"; + } + + result_t operator()(const operator_::struct_::TryMember& n) { + auto id = n.op1().as().id(); + assert(! lhs); + + if ( auto f = n.op0().type().as().field(id); f->isOptional() ) { + auto attr = memberAccess(n, id); + + if ( auto d = f->default_() ) + return memberAccess(n, fmt("value_or(%s)", cg->compile(*d))); + + return fmt("hilti::rt::struct_::value_or_exception(%s, \"%s\")", attr, n.op0().meta().location().render()); + } + + return structMember(n, n.op1()); + } + + /// Union + + unsigned int unionFieldIndex(const Expression& op0, const Expression& op1) { + auto id = op1.as().id(); + return op0.type().as().index(id); + } + + result_t operator()(const operator_::union_::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::union_::Unequal& n) { return binary(n, "!="); } + + result_t operator()(const operator_::union_::MemberConst& n) { + auto idx = unionFieldIndex(n.op0(), n.op1()); + return fmt("hilti::rt::union_::get<%u>(%s)", idx, op0(n)); + } + + result_t operator()(const operator_::union_::MemberNonConst& n) { + auto idx = unionFieldIndex(n.op0(), n.op1()); + + if ( lhs ) + return fmt("hilti::rt::union_::get_proxy<%u>(%s)", idx, op0(n)); + else + return fmt("hilti::rt::union_::get<%u>(%s)", idx, op0(n)); + } + + result_t operator()(const operator_::union_::HasMember& n) { + auto idx = unionFieldIndex(n.op0(), n.op1()); + return fmt("(%s.index() == %u)", op0(n), idx); + } + + // Signed integer + + result_t operator()(const operator_::signed_integer::DecrPostfix& n) { return fmt("%s--", op0(n)); } + result_t operator()(const operator_::signed_integer::DecrPrefix& n) { return fmt("--%s", op0(n)); } + result_t operator()(const operator_::signed_integer::Difference& n) { return fmt("%s - %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::DifferenceAssign& n) { + return fmt("%s -= %s", op0(n), op1(n)); + } + result_t operator()(const operator_::signed_integer::Division& n) { return fmt("%s / %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::DivisionAssign& n) { return fmt("%s /= %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::Greater& n) { return fmt("%s > %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::GreaterEqual& n) { return fmt("%s >= %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::signed_integer::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::signed_integer::Lower& n) { return fmt("%s < %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::LowerEqual& n) { return fmt("%s <= %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::Modulo& n) { return fmt("%s %% %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::Multiple& n) { return fmt("%s * %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::MultipleAssign& n) { return fmt("%s *= %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::Power& n) { + return fmt("hilti::rt::pow(%s, %s)", op0(n), op1(n)); + } + result_t operator()(const operator_::signed_integer::SignNeg& n) { return fmt("(-%s)", op0(n)); } + result_t operator()(const operator_::signed_integer::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::SumAssign& n) { return fmt("%s += %s", op0(n), op1(n)); } + result_t operator()(const operator_::signed_integer::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::signed_integer::CastSigned& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::signed_integer::CastUnsigned& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::signed_integer::CastReal& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + // Time + + result_t operator()(const operator_::time::CastFromReal& n) { return fmt("hilti::rt::Time(%f)", op0(n)); } + result_t operator()(const operator_::time::CastFromUnsignedInteger& n) { + return fmt("hilti::rt::Time(hilti::rt::integer::safe(%" PRIu64 ") * 1000000000)", op0(n)); + } + result_t operator()(const operator_::time::DifferenceInterval& n) { return binary(n, "-"); } + result_t operator()(const operator_::time::Equal& n) { return binary(n, "=="); } + result_t operator()(const operator_::time::Greater& n) { return binary(n, ">"); } + result_t operator()(const operator_::time::GreaterEqual& n) { return binary(n, ">="); } + result_t operator()(const operator_::time::Lower& n) { return binary(n, "<"); } + result_t operator()(const operator_::time::LowerEqual& n) { return binary(n, "<="); } + result_t operator()(const operator_::time::Nanoseconds& n) { return fmt("%s.nanoseconds()", op0(n)); } + result_t operator()(const operator_::time::Seconds& n) { return fmt("%s.seconds()", op0(n)); } + result_t operator()(const operator_::time::SumInterval& n) { return binary(n, "+"); } + result_t operator()(const operator_::time::SumTime& n) { return binary(n, "+"); } + result_t operator()(const operator_::time::Unequal& n) { return binary(n, "!="); } + + // Tuple + + result_t operator()(const operator_::tuple::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::tuple::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::tuple::Index& n) { + auto i = n.op1().as().ctor().as().value(); + return fmt("std::get<%u>(%s)", i, op0(n)); + } + + result_t operator()(const operator_::tuple::Member& n) { + auto id = n.op1().as().id(); + auto elem = n.op0().type().as().elementByID(id); + assert(elem); + return fmt("std::get<%u>(%s)", elem->first, op0(n)); + } + + // Unigned integer + + result_t operator()(const operator_::unsigned_integer::BitAnd& n) { return fmt("(%s & %s)", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::BitOr& n) { return fmt("(%s | %s)", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::BitXor& n) { return fmt("(%s ^ %s)", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::DecrPostfix& n) { return fmt("%s--", op0(n)); } + result_t operator()(const operator_::unsigned_integer::DecrPrefix& n) { return fmt("--%s", op0(n)); } + result_t operator()(const operator_::unsigned_integer::Difference& n) { return fmt("%s - %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::DifferenceAssign& n) { + return fmt("%s -= %s", op0(n), op1(n)); + } + result_t operator()(const operator_::unsigned_integer::Division& n) { return fmt("%s / %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::DivisionAssign& n) { + return fmt("%s /= %s", op0(n), op1(n)); + } + result_t operator()(const operator_::unsigned_integer::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::Greater& n) { return fmt("%s > %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::GreaterEqual& n) { return fmt("%s >= %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::unsigned_integer::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::unsigned_integer::Lower& n) { return fmt("%s < %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::LowerEqual& n) { return fmt("%s <= %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::Modulo& n) { return fmt("%s %% %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::Multiple& n) { return fmt("%s * %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::MultipleAssign& n) { + return fmt("%s *= %s", op0(n), op1(n)); + } + result_t operator()(const operator_::unsigned_integer::Negate& n) { return fmt("~%s", op0(n)); } + result_t operator()(const operator_::unsigned_integer::Power& n) { + return fmt("hilti::rt::pow(%s, %s)", op0(n), op1(n)); + } + result_t operator()(const operator_::unsigned_integer::ShiftLeft& n) { return fmt("%s << %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::ShiftRight& n) { return fmt("%s >> %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::SumAssign& n) { return fmt("%s += %s", op0(n), op1(n)); } + result_t operator()(const operator_::unsigned_integer::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::unsigned_integer::CastSigned& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::unsigned_integer::CastUnsigned& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + result_t operator()(const operator_::unsigned_integer::CastReal& n) { + auto t = n.op1().type().as().typeValue(); + return fmt("static_cast<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); + } + + // Vector + result_t operator()(const operator_::vector::iterator::IncrPostfix& n) { return fmt("%s++", op0(n)); } + result_t operator()(const operator_::vector::iterator::IncrPrefix& n) { return fmt("++%s", op0(n)); } + result_t operator()(const operator_::vector::iterator::Deref& n) { return fmt("*%s", op0(n)); } + result_t operator()(const operator_::vector::iterator::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::vector::iterator::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + result_t operator()(const operator_::vector::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::vector::IndexConst& n) { return fmt("%s[%s]", op0(n), op1(n)); } + result_t operator()(const operator_::vector::IndexNonConst& n) { return fmt("%s[%s]", op0(n), op1(n)); } + result_t operator()(const operator_::vector::Size& n) { return fmt("%s.size()", op0(n)); } + result_t operator()(const operator_::vector::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + result_t operator()(const operator_::vector::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } + result_t operator()(const operator_::vector::SumAssign& n) { return fmt("%s += %s", op0(n), op1(n)); } + + result_t operator()(const operator_::vector::Back& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.back()", self); + } + + result_t operator()(const operator_::vector::Front& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.front()", self); + } + + result_t operator()(const operator_::vector::PushBack& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.emplace_back(%s)", self, args[0]); + } + + result_t operator()(const operator_::vector::Reserve& n) { + auto [self, args] = methodArguments(n); + return fmt("%s.reserve(%s)", self, args[0]); + } + + // Weak reference + result_t operator()(const operator_::weak_reference::Deref& n) { return fmt("(*%s)", op0(n)); } + result_t operator()(const operator_::weak_reference::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::weak_reference::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } + + // Value reference + result_t operator()(const operator_::value_reference::Deref& n) { return fmt("(*%s)", op0(n)); } + result_t operator()(const operator_::value_reference::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } + result_t operator()(const operator_::value_reference::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } +}; + +} // anonymous namespace + +cxx::Expression CodeGen::compile(const expression::ResolvedOperator& o, bool lhs) { + if ( auto x = Visitor(this, lhs).dispatch(Expression(o)) ) + return cxx::Expression(*x); + + hilti::render(std::cerr, Expression(o)); + logger().internalError(fmt("operator failed to compile: %s", detail::renderOperatorPrototype(o))); +} diff --git a/hilti/src/compiler/codegen/statements.cc b/hilti/src/compiler/codegen/statements.cc new file mode 100644 index 000000000..1642cf24d --- /dev/null +++ b/hilti/src/compiler/codegen/statements.cc @@ -0,0 +1,399 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +using namespace hilti::detail; + +inline auto traceStatement(CodeGen* cg, cxx::Block* b, const Statement& s) { + if ( s.isA() ) + return; + + if ( cg->options().debug_location ) + b->addStatement(fmt("hilti::rt::debug::setLocation(\"%s\")", s.meta().location())); + + if ( cg->options().debug_trace ) + b->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-trace", "%s: %s"))", s.meta().location(), + util::escapeUTF8(fmt("%s", s), true))); +} + +namespace { + +struct Visitor : hilti::visitor::PreOrder { + Visitor(CodeGen* cg, cxx::Block* b) : cg(cg), block(b) {} + CodeGen* cg; + + int level = 0; + cxx::Block* block; + + void operator()(const statement::Assert& n) { + std::string throw_; + + if ( n.message() ) + throw_ = fmt("throw hilti::rt::AssertionFailure(hilti::rt::to_string_for_print(%s), \"%s\")", + cg->compile(*n.message()), n.meta().location()); + else { + auto msg = std::string(to_node(n.expression())); + throw_ = fmt(R"(throw hilti::rt::AssertionFailure("failed expression '%s'", "%s"))", + util::escapeUTF8(msg, true), n.meta().location()); + } + + if ( ! n.expectsException() ) { + cxx::Block stmt; + + if ( cg->options().debug_flow ) + stmt.addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: assertion error"))", n.meta().location())); + + stmt.addStatement(throw_); + block->addIf(fmt("! (%s)", cg->compile(n.expression())), cxx::Block(std::move(stmt))); + } + else { + if ( n.exception() ) + logger().internalError("not support currently for testing for specific exception in assertion", n); + + cxx::Block try_body; + try_body.addTmp( + cxx::declaration::Local{.id = "_", .type = "hilti::rt::exception::DisableAbortOnExceptions"}); + try_body.addStatement(fmt("%s", cg->compile(n.expression()))); + + if ( cg->options().debug_flow ) + try_body.addStatement( + fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: assertion error"))", n.meta().location())); + + try_body.addStatement(throw_); + + cxx::Block catch_rethrow; + catch_rethrow.addStatement("throw"); // dummy to make it non-empty; + + cxx::Block catch_cont; + catch_cont.addStatement(""); // dummy to make it non-empty; + + block->addTry(std::move(try_body), + { + {{.id = "", .type = "const hilti::rt::AssertionFailure&"}, catch_rethrow}, + {{.id = "", .type = "const hilti::rt::Exception&"}, catch_cont}, + }); + } + } + + void operator()(const statement::Block& n) { + if ( level == 0 ) { + ++level; + + for ( const auto& s : n.statements() ) { + traceStatement(cg, block, s); + dispatch(s); + } + + --level; + } + + else + block->addBlock(cg->compile(n)); + } + + void operator()(const statement::Break& n, position_t p) { block->addStatement("break"); } + + void operator()(const statement::Continue& n, position_t p) { block->addStatement("continue"); } + + void operator()(const statement::Comment& n) { + auto sep_before = (n.separator() == statement::comment::Separator::Before || + n.separator() == statement::comment::Separator::BeforeAndAfter); + auto sep_after = (n.separator() == statement::comment::Separator::After || + n.separator() == statement::comment::Separator::BeforeAndAfter); + + block->addComment(n.comment(), sep_before, sep_after); + } + + void operator()(const statement::Declaration& n) { + auto d = n.declaration().tryAs(); + + if ( ! d ) + logger().internalError("statements can only declare local variables"); + + std::vector args; + + auto t = d->type(); + if ( type::isReferenceType(t) ) + t = t.dereferencedType(); + + if ( auto s = t.tryAs() ) + args = cg->compileCallArguments(d->typeArguments(), s->parameters()); + + std::optional init; + + if ( auto i = d->init() ) + init = cg->compile(*i); + else + init = cg->typeDefaultValue(d->type()); + + auto l = cxx::declaration::Local{.id = cxx::ID(d->id()), + .type = cg->compile(d->type(), codegen::TypeUsage::Storage), + .args = std::move(args), + .init = init}; + + block->addLocal(l); + } + + void operator()(const statement::Expression& n) { block->addStatement(cg->compile(n.expression())); } + + void operator()(const statement::If& n) { + std::string init; + std::string cond; + + if ( auto x = n.init() ) { + auto& l = x->template as(); + std::optional cxx_init; + + if ( auto i = l.init() ) + cxx_init = cg->compile(*i); + else + cxx_init = cg->typeDefaultValue(l.init()->type()); + + init = fmt("%s %s", cg->compile(l.init()->type(), codegen::TypeUsage::Storage), x->id()); + + if ( cxx_init ) + init += fmt(" = %s", *cxx_init); + } + + if ( n.condition() ) + cond = cg->compile(*n.condition()); + + std::string head; + + if ( ! init.empty() && ! cond.empty() ) + head = fmt("%s; %s", init, cond); + else if ( ! init.empty() ) + head = init; + else + head = cond; + + if ( ! n.false_() ) + block->addIf(head, cg->compile(n.true_())); + else + block->addIf(head, cg->compile(n.true_()), cg->compile(*n.false_())); + } + + void operator()(const statement::For& n) { + auto id = cxx::ID(n.id()); + auto seq = cg->compile(n.sequence()); + auto body = cg->compile(n.body()); + + if ( ! n.sequence().isTemporary() ) + block->addForRange(true, id, fmt("%s", seq), body); + else { + cxx::Block b; + b.setEnsureBracesforBlock(); + b.addTmp(cxx::declaration::Local{.id = "__seq", .type = "auto", .init = seq}); + b.addForRange(true, id, fmt("hilti::rt::safe_range(__seq)"), body); + block->addBlock(std::move(b)); + } + } + + void operator()(const statement::Return& n, position_t p) { + if ( cg->options().debug_flow ) + block->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: return"))", n.meta().location())); + + if ( auto e = n.expression() ) + block->addStatement(fmt("return %s", cg->compile(*e))); + else + block->addStatement("return"); + } + + void operator()(const statement::Switch& n, position_t p) { + // TODO(robin): We generate if-else chain here. We could optimize the case + // where all expressions are integers and go with a "real" switch in + // that case. + cxx::ID cxx_id; + std::string cxx_type; + std::string cxx_init; + + if ( n.init() ) { + auto init = n.init()->as(); + cxx_type = cg->compile(n.type(), codegen::TypeUsage::Storage); + cxx_id = cxx::ID(init.id()); + cxx_init = cg->compile(*init.init()); + } + else { + cxx_type = cxx::ID("const auto"); + cxx_id = cxx::ID("__x"); + cxx_init = cg->compile(n.expression()); + } + + bool first = true; + for ( const auto& c : n.cases() ) { + std::string cond; + + auto exprs = c.preprocessedExpressions(); + + if ( exprs.size() == 1 ) + cond = cg->compile(exprs.front()); + else + cond = util::join(util::transform(exprs, [&](auto& e) { return cg->compile(e); }), " || "); + + auto body = cg->compile(c.body()); + + if ( first ) { + block->addIf(fmt("%s %s = %s", cxx_type, cxx_id, cxx_init), cond, body); + first = false; + } + else + block->addElseIf(cond, body); + } + + if ( auto d = n.default_() ) + block->addElse(cg->compile(d->body())); + else { + cxx::Block throw_; + throw_.addStatement(fmt("throw hilti::rt::UnhandledSwitchCase(hilti::rt::to_string_for_print(%s), \"%s\")", + cxx_id, n.meta().location())); + block->addElse(std::move(throw_)); + } + } + + void operator()(const statement::Throw& n, position_t p) { + if ( cg->options().debug_flow ) { + if ( auto e = n.expression() ) + block->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: throw %s)", n.meta().location(), *e)); + else + block->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: throw)", n.meta().location())); + } + + if ( auto e = n.expression() ) + block->addStatement(fmt("throw %s", cg->compile(*e))); + else + block->addStatement("throw"); + } + + void operator()(const statement::Try& n, position_t p) { + std::vector> catches; + + for ( const auto& c : n.catches() ) { + cxx::declaration::Argument arg; + + if ( auto p = c.parameter() ) { + auto t = cg->compile(p->type(), codegen::TypeUsage::InParameter); + arg = {.id = cxx::ID(p->id()), .type = std::move(t)}; + } + else + arg = {.id = "", .type = cxx::Type("const hilti::rt::UserException&")}; + + catches.emplace_back(std::move(arg), cg->compile(c.body())); + } + + block->addTry(cg->compile(n.body()), std::move(catches)); + } + + void operator()(const statement::While& n) { + std::optional init; + std::optional cxx_init; + + if ( n.init() ) + init = n.init()->as(); + + if ( init ) { + if ( auto i = init->init() ) + cxx_init = cg->compile(*i); + else + cxx_init = cg->typeDefaultValue(init->type()); + } + + if ( n.else_() ) { + // We generate different code if we have an "else" clause. + cxx::Block inner_wrapper; + + if ( ! n.condition() ) + inner_wrapper.addStatement(fmt("%s = %s", init->id(), *cxx_init)); + + auto else_ = cg->compile(*n.else_()); + else_.addStatement("break"); + + if ( n.condition() ) + inner_wrapper.addIf(fmt("! (%s)", cg->compile(*n.condition())), else_); + else + inner_wrapper.addIf(fmt("! %s", init->id()), else_); + + inner_wrapper.appendFromBlock(cg->compile(n.body())); + + cxx::Block outer_wrapper; + + if ( init ) { + if ( n.condition() ) + outer_wrapper.addLocal({.id = cxx::ID(init->id()), + .type = cg->compile(init->type(), codegen::TypeUsage::Storage), + .init = cxx_init}); + else + outer_wrapper.addLocal( + {.id = cxx::ID(init->id()), .type = cg->compile(init->type(), codegen::TypeUsage::Storage)}); + } + + outer_wrapper.addWhile(cxx::Expression("true"), inner_wrapper); + block->addBlock(outer_wrapper); + return; + } + + std::string sinit; + std::string cond; + + if ( init ) { + std::string cxx_init_lhs; + std::string cxx_init_rhs; + + cxx_init_lhs = fmt("%s %s", cg->compile(init->type(), codegen::TypeUsage::Storage), init->id()); + + if ( cxx_init ) + cxx_init_rhs = fmt(" = %s", *cxx_init); + + sinit = (cxx_init_lhs + cxx_init_rhs); + } + + if ( n.condition() ) + cond = cg->compile(*n.condition()); + + auto body = cg->compile(n.body()); + + if ( sinit.empty() ) + block->addWhile(cond, body); + + else if ( cond.empty() ) + block->addWhile(sinit, body); + + else + // C++ doesn't support having both init and cond in a while-loop. + // Use a for-loop instead. + block->addFor(sinit, cond, "", body); + } + + void operator()(const statement::Yield& n) { + if ( cg->options().debug_flow ) + block->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: yield"))", n.meta().location())); + + block->addStatement("hilti::rt::detail::yield()"); + } +}; + +} // anonymous namespace + +cxx::Block CodeGen::compile(const hilti::Statement& s, cxx::Block* b) { + if ( b ) { + pushCxxBlock(b); + traceStatement(this, b, s); + Visitor(this, b).dispatch(s); + popCxxBlock(); + return *b; + } + + auto block = cxx::Block(); + pushCxxBlock(&block); + traceStatement(this, &block, s); + Visitor(this, &block).dispatch(s); + popCxxBlock(); + return block; +} diff --git a/hilti/src/compiler/codegen/types.cc b/hilti/src/compiler/codegen/types.cc new file mode 100644 index 000000000..9e3b0176c --- /dev/null +++ b/hilti/src/compiler/codegen/types.cc @@ -0,0 +1,845 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using namespace hilti::detail; +using namespace hilti::detail::codegen; + +using util::fmt; + +namespace { + +struct VisitorDeclaration : hilti::visitor::PreOrder { + VisitorDeclaration(CodeGen* cg, util::Cache* cache) : cg(cg), cache(cache) {} + + CodeGen* cg; + util::Cache* cache; + std::list dependencies; + + void addDependency(const Type& t) { + for ( auto&& t : cg->typeDependencies(t) ) + dependencies.push_back(std::move(t)); + } + + result_t operator()(const type::Struct& n, const position_t p) { + auto scope = cxx::ID{cg->unit()->cxxNamespace()}; + auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("struct_%p", &n))}; + + if ( sid.namespace_() ) + scope = scope.namespace_(); + + auto id = cxx::ID(scope, sid); + + return cache->getOrCreate( + id, + []() { + // Just return an empty dummy for now to avoid cyclic recursion. + return cxx::declaration::Type{.id = "", .type = ""}; + }, + [&](auto& dummy) { + std::vector args; + std::vector fields; + std::vector> init_args; + std::vector init_stmts; + + cg->enablePrioritizeTypes(); + + cxx::Type type; + cxx::Type internal_type; + + for ( const auto& p : n.parameters() ) { + type = cg->compile(p.type(), codegen::TypeUsage::InParameter); + internal_type = cg->compile(p.type(), codegen::TypeUsage::Storage); + + if ( type::isReferenceType(p.type()) ) { + // We turn reference types into weak references for + // storage so that copying a struct won't cause + // potentially expensive copies or let us hold on to + // objects longer than they'd otherwise stick around. + assert(type::isReferenceType(p.type())); + internal_type = cg->compile(type::WeakReference(p.type().dereferencedType(), p.meta()), + codegen::TypeUsage::Storage); + } + + auto arg = cxx::declaration::Argument{.id = cxx::ID(fmt("__p_%s", p.id())), + .type = type, + .internal_type = internal_type}; + args.emplace_back(std::move(arg)); + } + + for ( const auto& f : n.fields() ) { + if ( f.isNoEmit() ) + continue; + + if ( auto ft = f.type().tryAs() ) { + auto d = cg->compile(f.id(), *ft, declaration::Linkage::Struct, + function::CallingConvention::Standard, f.attributes()); + + if ( f.isStatic() ) + d.linkage = "static"; + + if ( ft->flavor() == type::function::Flavor::Hook ) { + auto tid = n.typeID(); + + if ( ! tid ) + logger().internalError("Struct type with hooks does not have a type ID"); + + auto id_module = tid->sub(-2); + auto id_class = tid->sub(-1); + auto id_local = f.id(); + + if ( id_module.empty() ) + id_module = cg->hiltiUnit()->id(); + + auto id_hook = cxx::ID(cg->options().cxx_namespace_intern, id_module, + fmt("__hook_%s_%s", id_class, id_local)); + auto id_type = cxx::ID(id_module, id_class); + + auto args = util::transform(d.args, [](auto& a) { return a.id; }); + args.emplace_back("__self"); + + auto method_body = cxx::Block(); + auto self = cxx::declaration::Local{.id = "__self", + .type = "auto", + .init = fmt("hilti::rt::ValueReference<%s>::self(this)", + id_type)}; + method_body.addLocal(self); + method_body.addStatement(fmt("return %s(%s)", id_hook, util::join(args, ", "))); + + auto method_impl = cxx::Function{.declaration = d, .body = std::move(method_body)}; + + method_impl.declaration.id = cxx::ID(scope, sid, f.id()); + method_impl.declaration.linkage = "inline"; + cg->unit()->add(method_impl); + + std::list aux_types = { + cxx::declaration::Type{.id = cxx::ID(cg->options().cxx_namespace_intern, id_module, + id_class), + .type = fmt("struct %s", id_class), + .forward_decl = true}}; + + for ( const auto& p : ft->parameters() ) { + for ( auto t : cg->typeDependencies(p.type()) ) + aux_types.push_back(std::move(t)); + } + + auto hook = cxx::linker::Join{.id = cxx::ID(id_hook), + .callee = d, + .aux_types = aux_types, + .declare_only = true}; + + hook.callee.args.push_back( + cxx::declaration::Argument{.id = "__self", + .type = cg->compile(type::ValueReference(n), + codegen::TypeUsage::InOutParameter)}); + cg->unit()->add(hook); + } + + fields.emplace_back(std::move(d)); + continue; + } + + auto t = cg->compile(f.type(), codegen::TypeUsage::Storage); + + if ( f.isOptional() ) + t = fmt("std::optional<%s>", t); + + auto x = cxx::declaration::Local{.id = cxx::ID(f.id()), + .type = t, + .linkage = (f.isStatic() ? "inline static" : "")}; + + fields.emplace_back(std::move(x)); + + if ( ! f.isOptional() ) { + std::optional default_; + + if ( auto x = f.default_() ) + default_ = cg->compile(*x); + else + default_ = cg->typeDefaultValue(f.type()); + + if ( default_ ) + init_stmts.emplace_back(fmt("%s = %s", cxx::ID(f.id()), *default_)); + } + + cxx::Type init_arg_type = (f.isOptional() ? t : cxx::Type(fmt("std::optional<%s>", t))); + std::string init_arg_init; + auto fid = cxx::ID(f.id()); + auto __fid = cxx::ID(fmt("__%s", f.id())); + + if ( ! f.isInternal() ) { + // Depending on whether &optional anr/or &default have + // been given, the initialization of the field needs to + // be somewhat different. + if ( f.isOptional() ) + init_arg_init = fmt("%s = %s", fid, __fid); + else if ( auto x = f.default_() ) + init_arg_init = fmt("%s = %s ? *%s : %s", fid, __fid, __fid, cg->compile(*x)); + else if ( auto x = cg->typeDefaultValue(f.type()) ) + init_arg_init = fmt("%s = %s ? *%s : %s", fid, __fid, __fid, *x); + else + init_arg_init = fmt("if ( %s ) %s = *%s", __fid, fid, __fid); + + auto init_arg = cxx::declaration::Argument{.id = __fid, .type = init_arg_type}; + init_args.emplace_back(init_arg, init_arg_init); + } + } + + if ( init_args.size() ) { + // Add member initialization function __init(). + auto init_init_args = std::vector(); + auto init_init_body = cxx::Block(); + + for ( const auto& a : init_args ) { + init_init_args.push_back(a.first); + init_init_body.addStatement(a.second); + } + + auto init_init_decl = + cxx::declaration::Function{.id = cxx::ID(sid), .args = init_init_args, .linkage = "inline"}; + fields.emplace_back(init_init_decl); + + auto init_init_impl = + cxx::Function{.declaration = cxx::declaration::Function{.id = cxx::ID(scope, sid, sid.local()), + .args = init_init_args, + .linkage = "inline"}, + .body = std::move(init_init_body)}; + + cg->unit()->add(init_init_impl); + } + + // Add __init function that initializes fields to their + // defaults. Note that we can't just init the fields directly + // inline, as not all types may be defined at this point. + auto init_default_body = cxx::Block(std::move(init_stmts)); + auto init_default_decl = + cxx::declaration::Function{.result = "void", .id = "__init", .linkage = "inline"}; + fields.emplace_back(init_default_decl); + + auto init_default_impl = + cxx::Function{.declaration = cxx::declaration::Function{.result = "void", + .id = cxx::ID(scope, sid, "__init"), + .linkage = "inline"}, + .body = std::move(init_default_body)}; + + cg->unit()->add(init_default_impl); + + cg->disablePrioritizeTypes(); + + // Also add a forward declaration. + auto type_forward = cxx::declaration::Type{ + .id = id, + .type = fmt("struct %s", id), + .forward_decl = true, + .forward_decl_prio = true, + }; + + cg->unit()->add(type_forward); + dependencies.push_back(type_forward); + + auto t = cxx::type::Struct{.args = std::move(args), + .members = std::move(fields), + .type_name = cxx::ID(id.local()), + .add_ctors = true}; + return cxx::declaration::Type{.id = id, .type = t, .inline_code = t.inlineCode()}; + }); + + util::cannot_be_reached(); + } + + result_t operator()(const type::Tuple& n) { + for ( const auto& t : n.types() ) + addDependency(t); + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Union& n, const position_t p) { + auto scope = cxx::ID{cg->unit()->cxxNamespace()}; + auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("union_%p", &n))}; + + if ( sid.namespace_() ) + scope = scope.namespace_(); + + auto id = cxx::ID(scope, sid); + + // Add a forward declaration. + auto type_forward = cxx::declaration::Type{ + .id = id, + .type = fmt("struct %s", id.local()), + .forward_decl = true, + .forward_decl_prio = true, + }; + + cg->unit()->add(type_forward); + dependencies.push_back(type_forward); + + std::vector fields; + for ( const auto& f : n.fields() ) { + auto t = cg->compile(f.type(), codegen::TypeUsage::Storage); + auto x = cxx::declaration::Local{.id = cxx::ID(f.id()), .type = t}; + fields.emplace_back(std::move(x)); + } + + auto t = cxx::type::Union{.members = std::move(fields), .type_name = cxx::ID(id.local())}; + return cxx::declaration::Type{.id = id, .type = t}; + } + + result_t operator()(const type::Enum& n, const position_t p) { + auto scope = cxx::ID{cg->unit()->cxxNamespace()}; + auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("enum_%p", &n))}; + + if ( sid.namespace_() ) + scope = scope.namespace_(); + + auto id = cxx::ID(scope, sid); + + // Also add a forward declaration. + auto type_forward = cxx::declaration::Type{ + .id = id, + .type = fmt("enum class %s : int64_t", id.local()), + .forward_decl = true, + .forward_decl_prio = true, + }; + + cg->unit()->add(type_forward); + dependencies.push_back(type_forward); + + auto labels = util::transform(n.labels(), [](auto l) { return std::make_pair(cxx::ID(l.id()), l.value()); }); + auto t = cxx::type::Enum{.labels = std::move(labels), .type_name = cxx::ID(id.local())}; + return cxx::declaration::Type{.id = id, .type = t}; + } + + result_t operator()(const type::Exception& n, const position_t p) { + auto scope = cxx::ID{cg->unit()->cxxNamespace()}; + auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("exception_%p", &n))}; + + if ( sid.namespace_() ) + scope = scope.namespace_(); + + std::string base_ns = "hilti::rt"; + std::string base_cls = "UserException"; + + if ( auto b = n.baseType() ) { + auto x = cxx::ID(cg->compile(*b, codegen::TypeUsage::Ctor)); + base_ns = x.namespace_(); + base_cls = x.local(); + } + + auto id = cxx::ID(scope, sid); + return cxx::declaration::Type{.id = id, + .type = fmt("HILTI_EXCEPTION_NS(%s, %s, %s)", id.local(), base_ns, base_cls), + .no_using = true}; + } +}; + +struct VisitorStorage : hilti::visitor::PreOrder { + VisitorStorage(CodeGen* cg, util::Cache* cache, codegen::TypeUsage usage) + : cg(cg), cache(cache), usage(usage) {} + + CodeGen* cg; + util::Cache* cache; + codegen::TypeUsage usage; + + result_t operator()(const type::Address& n) { return CxxTypes{.base_type = "hilti::rt::Address"}; } + + result_t operator()(const type::Any& n) { return CxxTypes{.base_type = "std::any"}; } + + result_t operator()(const type::Bool& n) { return CxxTypes{.base_type = "bool"}; } + + result_t operator()(const type::Bytes& n) { return CxxTypes{.base_type = "hilti::rt::Bytes"}; } + + result_t operator()(const type::Real& n) { return CxxTypes{.base_type = "double"}; } + + result_t operator()(const type::Enum& n, const position_t p) { + if ( auto cxx = n.cxxID() ) + return CxxTypes{.base_type = cxx::Type(*cxx), .default_ = cxx::Expression(cxx::ID(*cxx, "Undef"))}; + + auto scope = cxx::ID{cg->unit()->cxxNamespace()}; + auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("enum_%p", &n))}; + + if ( sid.namespace_() ) + scope = scope.namespace_(); + + auto id = cxx::ID(scope, sid); + + // Add tailored to_string() function. + auto cases = util::transform(n.uniqueLabels(), [&](auto l) { + auto b = cxx::Block(); + b.addReturn(fmt("\"%s\"", cxx::ID(id.local(), l.id()))); + return std::make_pair(cxx::Expression(cxx::ID(id, l.id())), std::move(b)); + }); + + auto default_ = cxx::Block(); + default_.addReturn( + fmt(R"(hilti::rt::fmt("%s::", static_cast(x)))", id.local())); + + auto body = cxx::Block(); + body.addSwitch("x", cases, std::move(default_)); + + auto ts_decl = cxx::declaration::Function{.result = "std::string", + .id = {"hilti::rt::detail::adl", "to_string"}, + .args = {cxx::declaration::Argument{.id = "x", .type = cxx::Type(id)}, + cxx::declaration::Argument{.id = "", .type = "adl::tag"}}, + .linkage = "inline"}; + + auto ts_impl = cxx::Function{.declaration = ts_decl, .body = std::move(body)}; + + cg->unit()->add(ts_decl); + cg->unit()->add(ts_impl); + + // Add tailored operator<<. + auto render_body = cxx::Block(); + render_body.addStatement("o << hilti::rt::to_string(x); return o"); + + auto render_decl = + cxx::declaration::Function{.result = "std::ostream&", + .id = cxx::ID{fmt("%s::operator<<", id.namespace_())}, + .args = {cxx::declaration::Argument{.id = "o", .type = "std::ostream&"}, + cxx::declaration::Argument{.id = "x", .type = cxx::Type(id.local())}}}; + + auto render_impl = cxx::Function{.declaration = render_decl, .body = std::move(render_body)}; + + cg->unit()->add(render_decl); + cg->unit()->add(render_impl); + + cg->addDeclarationFor(n); + return CxxTypes{.base_type = std::string(sid), .default_ = cxx::Expression(cxx::ID(sid, "Undef"))}; + } + + result_t operator()(const type::Error& n) { return CxxTypes{.base_type = "hilti::rt::result::Error"}; } + + result_t operator()(const type::Exception& n, const position_t p) { + if ( auto cxx = n.cxxID() ) + return CxxTypes{.base_type = cxx::Type(*cxx), .default_ = cxx::Expression(cxx::ID(*cxx, "Undef"))}; + + cg->addDeclarationFor(n); + + auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("exception_%p", &n))}; + return CxxTypes{.base_type = std::string(sid), .default_ = cxx::Expression(cxx::ID(sid, "Undef"))}; + } + + result_t operator()(const type::Function& n) { return CxxTypes{}; } + + result_t operator()(const type::Interval& n) { return CxxTypes{.base_type = "hilti::rt::Interval"}; } + + result_t operator()(const type::bytes::Iterator& n) { + return CxxTypes{.base_type = "hilti::rt::bytes::SafeConstIterator"}; + } + + result_t operator()(const type::stream::Iterator& n) { + return CxxTypes{.base_type = "hilti::rt::stream::SafeConstIterator"}; + } + + result_t operator()(const type::list::Iterator& n) { + auto t = fmt("hilti::rt::List<%s>::iterator_t", cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::map::Iterator& n) { + auto i = (n.isConstant() ? "SafeConstIterator" : "SafeIterator"); + auto k = cg->compile(n.containerType().as().keyType(), codegen::TypeUsage::Storage); + auto v = cg->compile(n.containerType().as().elementType(), codegen::TypeUsage::Storage); + + auto t = fmt("hilti::rt::map::%s<%s, %s>", i, k, v); + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::set::Iterator& n) { + auto i = (n.isConstant() ? "SafeConstIterator" : "SafeIterator"); + auto x = cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage); + + auto t = fmt("hilti::rt::set::%s<%s>", i, x); + return CxxTypes{.base_type = fmt("%s", t)}; + } + + + result_t operator()(const type::vector::Iterator& n) { + auto i = (n.isConstant() ? "SafeConstIterator" : "SafeIterator"); + auto x = cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage); + + std::string allocator; + if ( auto def = cg->typeDefaultValue(n.dereferencedType()) ) + allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); + + auto t = fmt("hilti::rt::vector::%s<%s%s>", i, x, allocator); + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::Library& n) { return CxxTypes{.base_type = fmt("%s", n.cxxName())}; } + + result_t operator()(const type::List& n) { + std::string t; + + if ( n.elementType() == type::unknown ) + // Can only be the empty list. + t = "hilti::rt::list::Empty"; + else + t = fmt("hilti::rt::List<%s>", cg->compile(n.elementType(), codegen::TypeUsage::Storage)); + + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::Map& n) { + std::string t; + + if ( n.elementType() == type::unknown ) + // Can only be the empty map. + t = "hilti::rt::map::Empty"; + else { + auto k = cg->compile(n.keyType(), codegen::TypeUsage::Storage); + auto v = cg->compile(n.elementType(), codegen::TypeUsage::Storage); + t = fmt("hilti::rt::Map<%s, %s>", k, v); + } + + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::Network& n) { return CxxTypes{.base_type = "hilti::rt::Network"}; } + + result_t operator()(const type::Null& n) { return CxxTypes{.base_type = "hilti::rt::Null"}; } + + result_t operator()(const type::Port& n) { return CxxTypes{.base_type = "hilti::rt::Port"}; } + + result_t operator()(const type::RegExp& n) { return CxxTypes{.base_type = "hilti::rt::RegExp"}; } + + result_t operator()(const type::SignedInteger& n) { + cxx::Type t; + + switch ( n.width() ) { + case 8: t = "hilti::rt::integer::safe"; break; + case 16: t = "hilti::rt::integer::safe"; break; + case 32: t = "hilti::rt::integer::safe"; break; + case 64: t = "hilti::rt::integer::safe"; break; + default: logger().internalError("codegen: unexpected integer width", n); + } + + return CxxTypes{.base_type = t}; + } + + result_t operator()(const type::Set& n) { + std::string t; + + if ( n.elementType() == type::unknown ) + // Can only be the empty list. + t = "hilti::rt::set::Empty"; + else { + auto x = cg->compile(n.elementType(), codegen::TypeUsage::Storage); + t = fmt("hilti::rt::Set<%s>", x); + } + + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::Stream& n) { return CxxTypes{.base_type = "hilti::rt::Stream"}; } + + result_t operator()(const type::Union& n) { + if ( auto x = n.cxxID() ) + return CxxTypes{.base_type = cxx::Type(*x)}; + + auto scope = cxx::ID{cg->unit()->cxxNamespace().namespace_()}; + auto sid = cxx::ID{scope, (n.typeID() ? std::string(*n.typeID()) : fmt("union_%p", &n))}; + auto ns = sid.namespace_(); + + if ( cg->prioritizeTypes() ) + cg->unit()->prioritizeType(sid); + + return cache->getOrCreate( + sid, [&]() { return CxxTypes{.base_type = std::string(sid)}; }, + [&](auto& cxx_types) { + auto render_body = cxx::Block(); + render_body.addStatement("o << hilti::rt::to_string(x); return o"); + + auto render_decl = + cxx::declaration::Function{.result = "std::ostream&", + .id = cxx::ID{fmt("%s::operator<<", ns)}, + .args = {cxx::declaration::Argument{.id = "o", .type = "std::ostream&"}, + cxx::declaration::Argument{ + .id = "x", + .type = fmt("const %s&", sid), + }}}; + + auto render_impl = cxx::Function{.declaration = render_decl, .body = std::move(render_body)}; + + cg->unit()->add(render_decl); + cg->unit()->add(render_impl); + cg->addDeclarationFor(n); + + return cxx_types; + }); + } + + result_t operator()(const type::Vector& n) { + std::string t; + + if ( n.elementType() == type::unknown ) + // Can only be the empty list. + t = "hilti::rt::vector::Empty"; + else { + auto x = cg->compile(n.elementType(), codegen::TypeUsage::Storage); + + std::string allocator; + if ( auto def = cg->typeDefaultValue(n.elementType()) ) + allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); + + t = fmt("hilti::rt::Vector<%s%s>", x, allocator); + } + + return CxxTypes{.base_type = fmt("%s", t)}; + } + + result_t operator()(const type::Time& n) { return CxxTypes{.base_type = "hilti::rt::Time"}; } + + result_t operator()(const type::UnsignedInteger& n) { + cxx::Type t; + + switch ( n.width() ) { + case 8: + t = "hilti::rt::integer::safe"; + break; // 2 bytes to avoid overloading confusion with uchar_t + case 16: t = "hilti::rt::integer::safe"; break; + case 32: t = "hilti::rt::integer::safe"; break; + case 64: t = "hilti::rt::integer::safe"; break; + default: logger().internalError("codegen: unexpected integer width", n); + } + + return CxxTypes{.base_type = t}; + } + + result_t operator()(const type::Optional& n) { + std::string t; + + if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) + t = fmt("std::optional<%s>", cg->compile(ct, codegen::TypeUsage::Storage)); + else + t = "*"; + + return CxxTypes{.base_type = t}; + } + + result_t operator()(const type::StrongReference& n) { + std::string t; + + if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) + t = fmt("hilti::rt::StrongReference<%s>", cg->compile(ct, codegen::TypeUsage::Ctor)); // XXX + else + t = "*"; + + return CxxTypes{.base_type = t, .param_in = fmt("const %s", t), .param_inout = fmt("%s", t)}; + } + + result_t operator()(const type::stream::View& n) { return CxxTypes{.base_type = "hilti::rt::stream::View"}; } + + result_t operator()(const type::ResolvedID& n) { + if ( auto x = dispatch(n.type()) ) + return *x; + + logger().internalError(fmt("codegen: ID resolves to type %s, which does not have a visitor", + to_node(n.type()).render()), + n); + } + + result_t operator()(const type::Result& n) { + std::string t; + + if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) + t = fmt("hilti::rt::Result<%s>", cg->compile(ct, codegen::TypeUsage::Storage)); + else + t = "*"; + + return CxxTypes{.base_type = t}; + } + + result_t operator()(const type::String& n) { return CxxTypes{.base_type = "std::string"}; } + + result_t operator()(const type::Struct& n) { + if ( auto x = n.cxxID() ) + return CxxTypes{.base_type = cxx::Type(*x)}; + + auto scope = cxx::ID{cg->unit()->cxxNamespace().namespace_()}; + auto sid = cxx::ID{scope, (n.typeID() ? std::string(*n.typeID()) : fmt("struct_%p", &n))}; + auto ns = sid.namespace_(); + + if ( cg->prioritizeTypes() ) + cg->unit()->prioritizeType(sid); + + return cache->getOrCreate( + sid, [&]() { return CxxTypes{.base_type = std::string(sid)}; }, + [&](auto& cxx_types) { + auto render_body = cxx::Block(); + render_body.addStatement("o << hilti::rt::to_string(x); return o"); + + auto render_decl = + cxx::declaration::Function{.result = "std::ostream&", + .id = cxx::ID{fmt("%s::operator<<", ns)}, + .args = {cxx::declaration::Argument{.id = "o", .type = "std::ostream&"}, + cxx::declaration::Argument{ + .id = "x", + .type = fmt("const %s&", sid), + }}}; + + auto render_impl = cxx::Function{.declaration = render_decl, .body = std::move(render_body)}; + + cg->unit()->add(render_decl); + cg->unit()->add(render_impl); + cg->addDeclarationFor(n); + + return cxx_types; + }); + } + + result_t operator()(const type::Tuple& n) { + auto x = util::transform(n.types(), [this](auto t) { return cg->compile(t, codegen::TypeUsage::Storage); }); + auto t = fmt("std::tuple<%s>", util::join(x, ", ")); + return CxxTypes{.base_type = t}; + } + + result_t operator()(const type::UnresolvedID& n) { + logger().internalError(fmt("codgen: unresolved type ID %s", n.id()), n); + } + + result_t operator()(const type::Void& n) { return CxxTypes{.base_type = "void"}; } + + result_t operator()(const type::Computed& n) { + if ( auto x = dispatch(n.type()) ) + return *x; + + logger() + .internalError(fmt("codegen: type wrapper (computed) resolves to type %s, which does not have a visitor", + to_node(n.type()).render()), + n); + } + + result_t operator()(const type::WeakReference& n) { + std::string t; + + if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) + t = fmt("hilti::rt::WeakReference<%s>", cg->compile(ct, codegen::TypeUsage::Ctor)); + else + t = "*"; + + return CxxTypes{.base_type = t}; + } + + result_t operator()(const type::ValueReference& n) { + std::string t; + + if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) { + auto element_type = cg->compile(ct, codegen::TypeUsage::Ctor); + return CxxTypes{.base_type = fmt("hilti::rt::ValueReference<%s>", element_type), .ctor = element_type}; + } + else + return CxxTypes{.base_type = "*"}; + } +}; + +} // anonymous namespace + +cxx::Type CodeGen::compile(const hilti::Type& t, codegen::TypeUsage usage) { + auto x = VisitorStorage(this, &_cache_types_storage, usage).dispatch(t); + + if ( ! x ) + logger().internalError(fmt("codegen: type %s does not have a visitor", t), t); + + std::optional base_type; + if ( x->base_type && usage != codegen::TypeUsage::Ctor ) + base_type = *x->base_type; + + switch ( usage ) { + case codegen::TypeUsage::Storage: + if ( x->storage ) + return std::move(*x->storage); + + if ( base_type ) + return std::move(*base_type); + + logger().internalError(fmt("codegen: type %s does not support use as storage", to_node(t).render()), t); + break; + + case codegen::TypeUsage::CopyParameter: + if ( x->param_copy ) + return std::move(*x->param_copy); + + if ( base_type ) + return fmt("%s", *base_type); + + logger().internalError(fmt("codegen: type %s does not support use as copy-parameter ", to_node(t).render()), + t); + break; + + case codegen::TypeUsage::InParameter: + if ( x->param_in ) + return std::move(*x->param_in); + + if ( base_type ) + return fmt("const %s&", *base_type); + + logger().internalError(fmt("codegen: type %s does not support use as in-parameter ", to_node(t).render()), + t); + break; + + case codegen::TypeUsage::InOutParameter: + if ( x->param_inout ) + return std::move(*x->param_inout); + + if ( base_type ) + return fmt("%s&", *base_type); + + logger().internalError(fmt("codegen: type %s does not support use as inout-parameter ", + to_node(t).render()), + t); + break; + + case codegen::TypeUsage::FunctionResult: + if ( x->result ) + return std::move(*x->result); + + if ( base_type ) + return std::move(*base_type); + + logger().internalError(fmt("codegen: type %s does not support use as function result", to_node(t).render()), + t); + break; + + case codegen::TypeUsage::Ctor: + if ( x->ctor ) + return std::move(*x->ctor); + + if ( x->base_type ) + return std::move(*x->base_type); + + logger().internalError(fmt("codegen: type %s does not support use as storage", to_node(t).render()), t); + break; + + case codegen::TypeUsage::None: + logger().internalError(fmt("codegen: type compilation with 'None' usage", to_node(t).render()), t); + break; + default: util::cannot_be_reached(); + } +} + +std::optional CodeGen::typeDefaultValue(const hilti::Type& t) { + auto x = VisitorStorage(this, &_cache_types_storage, codegen::TypeUsage::None).dispatch(t); + + if ( ! x ) + logger().internalError(fmt("codegen: type %s does not have a visitor", t), t); + + return std::move(x->default_); +}; + +std::optional CodeGen::typeDeclaration(const hilti::Type& t) { + return VisitorDeclaration(this, &_cache_types_declarations).dispatch(t); +}; + +std::list CodeGen::typeDependencies(const hilti::Type& t) { + VisitorDeclaration v(this, &_cache_types_declarations); + v.dispatch(type::effectiveType(t)); + return v.dependencies; +}; diff --git a/hilti/src/compiler/codegen/unpack.cc b/hilti/src/compiler/codegen/unpack.cc new file mode 100644 index 000000000..a74ec9af6 --- /dev/null +++ b/hilti/src/compiler/codegen/unpack.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +using namespace hilti::detail; + +namespace { + +struct Visitor : hilti::visitor::PreOrder { + Visitor(CodeGen* cg, cxx::Expression data, const std::vector& args) + : cg(cg), data(std::move(data)), args(args) {} + CodeGen* cg; + cxx::Expression data; + const std::vector& args; + + result_t operator()(const type::Address& n) { + return fmt("hilti::rt::address::unpack(%s, %s, %s)", data, args[0], args[1]); + } + + result_t operator()(const type::UnsignedInteger& n) { + return fmt("hilti::rt::integer::unpack(%s, %s)", n.width(), data, args[0]); + } + + result_t operator()(const type::SignedInteger& n) { + return fmt("hilti::rt::integer::unpack(%s, %s)", n.width(), data, args[0]); + } + + result_t operator()(const type::Real& n) { + return fmt("hilti::rt::real::unpack(%s, %s, %s)", data, args[0], args[1]); + } +}; + +} // anonymous namespace + +cxx::Expression CodeGen::unpack(const hilti::Type& t, const Expression& data, const std::vector& args) { + auto cxx_args = util::transform(args, [&](const auto& e) { return compile(e, false); }); + if ( auto x = Visitor(this, compile(data), cxx_args).dispatch(t) ) + return cxx::Expression(*x); + + logger().internalError("unpack failed to compile", t); +} + +cxx::Expression CodeGen::unpack(const hilti::Type& t, const cxx::Expression& data, + const std::vector& args) { + if ( auto x = Visitor(this, data, args).dispatch(t) ) + return cxx::Expression(*x); + + logger().internalError("unpack failed to compile", t); +} diff --git a/hilti/src/compiler/coercion.cc b/hilti/src/compiler/coercion.cc new file mode 100644 index 000000000..f7e5cfe6f --- /dev/null +++ b/hilti/src/compiler/coercion.cc @@ -0,0 +1,442 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using namespace util; + +namespace hilti::logging::debug { +inline const DebugStream Resolver("resolver"); +} // namespace hilti::logging::debug + +// Public version going through all plugins. +Result hilti::coerceCtor(Ctor c, const Type& dst, bitmask style) { + if ( c.type() == dst ) + return std::move(c); + + for ( auto p : plugin::registry().plugins() ) { + if ( ! (p.coerce_ctor) ) + continue; + + if ( auto nc = (*p.coerce_ctor)(c, dst, style) ) + return *nc; + } + + return result::Error("could not coeerce type for constructor"); +} + +static Result _coerceType(const Type& src_, const Type& dst_, bitmask style) { + auto src = type::effectiveType(src_); + Type dst = type::effectiveType(dst_); + + // TODO(robin): Not sure if this should/must replicate all the type coercion + // login in coerceExpression(). If so, we should factor that out. + // Update: I believe the answer is yes ... Added a few more cases, but this will + // likely need more work. + + if ( src == dst ) + return dst; + + if ( ! type::isConstCompatible(src, dst) && ! (style & CoercionStyle::Assignment) ) + return result::Error("types' constness is incompatible"); + + if ( type::isParameterized(dst) && dst.isWildcard() && dst.typename_() == src.typename_() ) + return type::nonConstant(src_); + + if ( style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall) ) { + if ( auto opt = dst.tryAs() ) { + if ( dst.isWildcard() ) + return dst; + + // All types converts into a corresponding optional. + if ( auto x = coerceType(src_, opt->dereferencedType(), style) ) + return {type::Optional(*x, src.meta())}; + } + + if ( auto opt = dst.tryAs() ) { + if ( dst.isWildcard() ) + return dst; + + // All types converts into a corresponding result. + if ( auto x = coerceType(src_, opt->dereferencedType(), style) ) + return {type::Result(*x, src.meta())}; + } + + if ( auto x = dst.tryAs(); x && ! type::isReferenceType(src) ) { + // All types converts into a corresponding value_ref. + if ( auto y = coerceType(src_, x->dereferencedType(), style) ) + return {type::ValueReference(*x, src.meta())}; + } + } + + for ( auto p : plugin::registry().plugins() ) { + if ( ! (p.coerce_type) ) + continue; + + if ( auto nt = (*p.coerce_type)(type::nonConstant(src), type::nonConstant(dst), style) ) + return type::nonConstant(*nt); + } + + return result::Error("cannot coeerce types"); +} + +// Public version going through all plugins. +Result hilti::coerceType(const Type& src_, const Type& dst_, bitmask style) { + auto src = type::effectiveType(src_); + + if ( const auto& orig = src.originalNode(); (style & CoercionStyle::PreferOriginalType) && orig ) { + if ( auto nt = hilti::coerceType(type::effectiveType(orig->as()), dst_, style) ) + return *nt; + } + + return _coerceType(src_, dst_, style); +} + +std::string hilti::to_string(bitmask style) { + std::vector labels; + + if ( style & CoercionStyle::PreferOriginalType ) + labels.emplace_back("prefer-original-type"); + + if ( style & CoercionStyle::TryExactMatch ) + labels.emplace_back("try-exact-match"); + + if ( style & CoercionStyle::TryConstPromotion ) + labels.emplace_back("try-const-promotion"); + + if ( style & CoercionStyle::TryCoercion ) + labels.emplace_back("try-coercion"); + + if ( style & CoercionStyle::Assignment ) + labels.emplace_back("assignment"); + + if ( style & CoercionStyle::FunctionCall ) + labels.emplace_back("function-call"); + + if ( style & CoercionStyle::OperandMatching ) + labels.emplace_back("operand-matching"); + + if ( style & CoercionStyle::DisallowTypeChanges ) + labels.emplace_back("disallow-type-changes"); + + if ( style & CoercionStyle::ContextualConversion ) + labels.emplace_back("contextual-conversion"); + + return util::join(labels, ","); +}; + +Result>> hilti::coerceOperands(const std::vector& exprs, + const std::vector& operands, + bitmask style) { + int num_type_changes = 0; + bool changed = false; + std::vector transformed; + + if ( exprs.size() > operands.size() ) + return result::Error("more expressions than operands"); + + for ( auto [i, op] : util::enumerate(operands) ) { + if ( i >= exprs.size() ) { + // Running out of operands, must have a default or be optional. + if ( op.default_ ) { + transformed.push_back(*op.default_); + changed = true; + } + else if ( op.optional ) { + // transformed.push_back(hilti::expression::Ctor(hilti::ctor::Null())); + } + else + return result::Error("stray operand"); + + continue; + } + + auto oat = operator_::type(op.type, exprs, transformed); + + if ( ! oat ) + return result::Error("could not look up operand type"); + + auto result = coerceExpression(exprs[i], *oat, style); + + if ( result.coerced ) { + HILTI_DEBUG(logging::debug::Resolver, + util::fmt(" [param %d] matching %s against %s -> success: %s (coerced expression is %s) (%s)", + i, exprs[i].type(), *oat, result.coerced->type(), + (result.coerced->isConstant() ? "const" : "non-const"), + (result.consider_type_changed ? "type changed" : "type not changed"))); + } + else { + HILTI_DEBUG(logging::debug::Resolver, + util::fmt(" [param %d] matching %s against %s -> failure", i, exprs[i].type(), *oat)); + return result::Error("could not match coercion operands"); + } + + // We check if the primary type of the alternative has changed. Only + // one operand must change its primary type for an alternative to + // match. + if ( result.consider_type_changed && (++num_type_changes > 1 || style & CoercionStyle::DisallowTypeChanges) && + ! (style & CoercionStyle::FunctionCall) ) + return result::Error("no valid coercion found"); + + transformed.push_back(*result.coerced); + + if ( result.nexpr ) + changed = true; + } + + return std::make_pair(changed, transformed); +} + +static CoercedExpression _coerceExpression(const Expression& e, const Type& src_, const Type& dst_, + bitmask style) { + std::unique_ptr dbg_indent; + + if ( style & CoercionStyle::_Recursing ) + dbg_indent = std::make_unique(logging::debug::Resolver); + else + style |= CoercionStyle::_Recursing; + + const auto no_change = CoercedExpression(e); + auto src = type::effectiveType(src_); + auto dst = type::effectiveType(dst_); + CoercedExpression _result; + int _line = 0; + +#define RETURN(x) \ + { \ + _result = (x); \ + _line = __LINE__; \ + goto exit; \ + } + + if ( style & CoercionStyle::TryExactMatch ) { + if ( src == dst ) { + if ( e.isConstant() == type::isConstant(dst) ) + RETURN(no_change); + + if ( style & CoercionStyle::OperandMatching && ! type::isMutable(dst) ) + RETURN(no_change); + } + + if ( src.typename_() == dst.typename_() && e.isConstant() == type::isConstant(dst) ) { + if ( dst.isWildcard() ) + RETURN(no_change); + + if ( type::isParameterized(src) && type::isParameterized(dst) ) { + auto params1 = type::effectiveType(src).typeParameters(); + auto params2 = type::effectiveType(dst).typeParameters(); + + if ( params1.size() == params2.size() ) { + bool match = true; + for ( auto&& [p1, p2] : util::zip2(params1, params2) ) { + auto t1 = p1.tryAs(); + auto t2 = p2.tryAs(); + + if ( ! (t1 && t2) ) { + // Don't have a generic node comparision for the + // individual parameters, so just compare the + // whole types directly. + match = (src == dst); + break; + } + + t1 = type::effectiveType(*t1); + t2 = type::effectiveType(*t2); + + if ( const auto& orig = t1->originalNode(); + (style & CoercionStyle::PreferOriginalType) && orig ) + t1 = orig->as(); + + if ( const auto& orig = t2->originalNode(); + (style & CoercionStyle::PreferOriginalType) && orig ) + t2 = orig->as(); + + if ( ! coerceType(*t1, *t2, CoercionStyle::TryExactMatch) ) { + match = false; + break; + } + } + + if ( match ) + RETURN(no_change); + } + } + } + } + + if ( style & CoercionStyle::TryConstPromotion ) { + if ( style & (CoercionStyle::OperandMatching | CoercionStyle::FunctionCall) ) { + // Don't allow a constant value to match a non-constant operand. + if ( e.isConstant() && (! type::isConstant(dst)) && type::isMutable(dst) ) + RETURN(result::Error()); + + if ( dst.isWildcard() && src.typename_() == dst.typename_() ) + RETURN(no_change); + + if ( src == dst ) + RETURN(no_change); + + if ( type::sameExceptForConstness(src, dst) ) { + RETURN(no_change); + } + } + + if ( style & CoercionStyle::Assignment ) { + if ( src == dst ) + RETURN(no_change); + + if ( type::sameExceptForConstness(src, dst) ) + RETURN(no_change); + + if ( dst.isWildcard() && src.typename_() == dst.typename_() ) + RETURN(no_change); + } + } + + else { + if ( style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall) ) { + // Don't allow assigning to a constant. + if ( type::isConstant(dst) ) + RETURN(result::Error()); + } + + if ( style & CoercionStyle::OperandMatching ) { + // Don't allow a constant value to match a non-constant operand. + if ( e.isConstant() && ! (type::isConstant(dst) || ! type::isMutable(dst)) ) + RETURN(result::Error()); + } + } + + if ( dst.isA() ) + // type::Any accepts anything without actual coercion. + RETURN(no_change); + + if ( auto x = e.tryAs() ) { + // Make sure the expression remains a member expression, as we will + // be expecting to cast it to that. + if ( auto t = hilti::coerceType(x->type(), dst, style) ) { + RETURN(CoercedExpression(src_, expression::Member(x->id(), *t, x->meta()))); + } + else + RETURN(result::Error()); + } + + if ( auto o = dst.template tryAs() ) { + // Match tuple against operands according to function call rules. + HILTI_DEBUG(logging::debug::Resolver, util::fmt("matching against call parameters")); + logging::DebugPushIndent _(logging::debug::Resolver); + + auto c = e.template tryAs(); + if ( ! c ) + RETURN(CoercedExpression()); + + // TOOD(robin): Why do we need this block? We do a separate operand + // matching afterwards, too. + + if ( auto t = c->ctor().template tryAs() ) { + CoercionStyle function_style = + (style & CoercionStyle::TryCoercion ? CoercionStyle::TryAllForFunctionCall : + CoercionStyle::TryDirectMatchForFunctionCall); + if ( auto result = coerceOperands(t->value(), o->operands(), function_style) ) { + if ( result->first ) { + RETURN( + CoercedExpression(e.type(), expression::Ctor(hilti::ctor::Tuple(std::move(result->second))))); + } + else { + RETURN(no_change); + } + } + } + + RETURN(CoercedExpression()); + } + + if ( style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall) ) { + if ( auto opt = dst.tryAs() ) { + if ( opt->isWildcard() ) + RETURN(no_change); + + // All types converts into a corresponding optional. + if ( auto x = coerceExpression(e, opt->dereferencedType(), style) ) + RETURN(CoercedExpression(src_, expression::Coerced(*x.coerced, dst, e.meta()))); + } + + if ( auto result = dst.tryAs() ) { + if ( result->isWildcard() ) + RETURN(no_change); + + // All types convert into a corresponding result. + if ( auto x = coerceExpression(e, result->dereferencedType(), style) ) + RETURN(CoercedExpression(src_, expression::Coerced(*x.coerced, dst, e.meta()))); + } + + if ( auto x = dst.tryAs(); x && ! type::isReferenceType(src) ) { + // All types converts into a corresponding value_ref. + if ( auto y = coerceExpression(e, x->dereferencedType(), style) ) + RETURN(CoercedExpression(src_, expression::Coerced(*y.coerced, dst, e.meta()))); + } + } + + if ( style & CoercionStyle::TryCoercion ) { + if ( auto c = e.tryAs() ) { + if ( auto nc = hilti::coerceCtor(c->ctor(), dst, style) ) + RETURN(CoercedExpression(src_, expression::Ctor(ctor::Coerced(c->ctor(), *nc, c->meta()), e.meta()))); + } + + if ( auto t = hilti::coerceType(src, dst, style) ) + // We wrap the expression into a coercion even if the new type is + // the same as *dst*. That way the overloader has a way to + // recognize that the types aren't identical. + RETURN(CoercedExpression(src_, expression::Coerced(e, *t, e.meta()))); + } + + _result = result::Error(); + +exit: + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("coercing %s %s (%s) to %s%s (%s) -> %s [%s] (%s) (#%d)", + (e.isConstant() ? "const" : "non-const"), to_node(src), + util::replace(src.typename_(), "hilti::type::", ""), + (type::isConstant(dst) ? "" : "non-const "), to_node(dst), + util::replace(dst.typename_(), "hilti::type::", ""), + (_result ? + util::fmt("%s %s (%s)", (_result.coerced->isConstant() ? "const" : "non-const"), + _result.coerced->type(), + util::replace(_result.coerced->type().typename_(), "hilti::type::", "")) : + "fail"), + to_string(style), e.meta().location(), _line)); + + return _result; +} + +CoercedExpression hilti::coerceExpression(const Expression& e, const Type& src_, const Type& dst_, + bitmask style) { + auto src = type::effectiveType(src_); + + if ( const auto& orig = src.originalNode(); (style & CoercionStyle::PreferOriginalType) && orig ) { + if ( auto nt = hilti::coerceExpression(e, type::effectiveType(orig->as()), dst_, style) ) + return nt; + } + + return _coerceExpression(e, src_, dst_, style); +} + +CoercedExpression hilti::coerceExpression(const Expression& e, const Type& dst, bitmask style) { + return coerceExpression(e, e.type(), dst, style); +} diff --git a/hilti/src/compiler/context.cc b/hilti/src/compiler/context.cc new file mode 100644 index 000000000..965ec94c7 --- /dev/null +++ b/hilti/src/compiler/context.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace hilti; +using namespace hilti::context; + +namespace hilti::logging::debug { +inline const DebugStream Compiler("compiler"); +} // namespace hilti::logging::debug + +Result Options::parseDebugAddl(const std::string& flags) { + for ( auto i : util::split(flags, ",") ) { + i = util::trim(i); + + if ( i.empty() ) + continue; + else if ( i == "trace" ) + debug_trace = true; + else if ( i == "flow" ) + debug_flow = true; + else if ( i == "location" ) + debug_location = true; + else + return result::Error( + util::fmt("unknow codegen debug option '%s', must be 'flow' or 'trace' or 'location'", i)); + } + + return Nothing(); +} + +Context::Context(Options options) : _options(std::move(std::move(options))) { + operator_::Registry::singleton().printDebug(); +} + +const CachedModule& Context::registerModule(const ModuleIndex& idx, Node&& module, bool requires_compilation) { + auto id = module.as().id(); + if ( _module_cache_by_id.find(id) != _module_cache_by_id.end() ) + logger().internalError(util::fmt("module '%s' registered more than once in context", id)); + + HILTI_DEBUG(logging::debug::Compiler, util::fmt("registering AST for module %s (%s)", idx.id, idx.path)); + + _modules.emplace_back(std::make_unique(std::move(module)), nullptr); + auto cached = std::make_shared(idx, NodeRef(*_modules.back().first)); + cached->requires_compilation = requires_compilation; + _modules.back().second = cached; + _module_cache_by_id.insert({idx.id, cached}); + + if ( ! idx.path.empty() ) + _module_cache_by_path.insert({idx.path, cached}); + + return *cached; +} + +void Context::updateModule(const CachedModule& module) { + assert(module.index.id); + + auto i = _module_cache_by_id.find(module.index.id); + if ( i == _module_cache_by_id.end() ) + logger().internalError(util::fmt("module '%s' to update has not been registered", module.index.id)); + + const auto& cached = i->second; + + if ( cached->node->identity() != module.node->identity() ) + logger().internalError("updating module with name of existing but different AST"); + + *cached = module; + + std::string deps = "n/a"; + if ( cached->dependencies ) { + deps = util::join(util::transform(*cached->dependencies, [](auto idx) { return idx.id; }), ", "); + if ( deps.empty() ) + deps = "(none)"; + } + + std::string requires_compilation = (cached->requires_compilation ? "yes" : "no"); + std::string final = (cached->final ? "yes" : "no"); + + HILTI_DEBUG(logging::debug::Compiler, + util::fmt("updated cached AST for module %s (final: %s, requires_compilation: %s, dependencies: %s)", + cached->index.id, final, requires_compilation, deps)); +} + +std::optional Context::lookupModule(const ID& id) { + if ( auto x = _module_cache_by_id.find(id); x != _module_cache_by_id.end() ) + return *x->second; + else + return {}; +} + +std::optional Context::lookupModule(const std::filesystem::path& path) { + if ( auto x = _module_cache_by_path.find(util::normalizePath(path)); x != _module_cache_by_path.end() ) + return *x->second; + else + return {}; +} + +std::vector Context::lookupDependenciesForModule(const ID& id) { + auto m = lookupModule(id); + if ( ! m ) + return {}; + + if ( ! m->dependencies ) + return {}; + + std::vector deps; + + for ( const auto& x : *m->dependencies ) { + auto d = lookupModule(x.id); + if ( ! d ) + return {}; // Shouldn't really happen. Assert? + + deps.push_back(*d); + } + + return deps; +} diff --git a/hilti/src/compiler/cxx/elements.cc b/hilti/src/compiler/cxx/elements.cc new file mode 100644 index 000000000..ce346a6f3 --- /dev/null +++ b/hilti/src/compiler/cxx/elements.cc @@ -0,0 +1,686 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti; +using namespace hilti::detail; +using namespace hilti::detail::cxx::formatter; + +using nlohmann::json; +using util::fmt; + +namespace flags { +static const unsigned int BlockEos = (1U << 0U); // Add an end-of-statement after block (i.e., ';'). +static const unsigned int NoSeparator = (1U << 1U); // Don't add a separator after block. +static const unsigned int AddSeparatorAfter = (1U << 2U); // Force adding a separator after block. +static const unsigned int AddSeparatorBefore = (1U << 4U); // Force adding a separator before block. +} // namespace flags + + +std::string cxx::normalize_id(std::string id) { + static const std::set reserved = {"and", + "and_eq", + "asm", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "char8_t", + "char16_t", + "char32_t", + "class", + "compl", + "concept", + "const", + "consteval", + "constexpr", + "constinit", + "const_cast", + "continue", + "co_await", + "co_return", + "co_yield", + "decltype", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "noexcept", + "not", + "not_eq", + "nullptr", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "requires", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "static_cast", + "struct", + "switch", + "template", + "this", + "NULL"}; + + auto map = [](auto id) { + if ( reserved.find(id) != reserved.end() ) + id.append("_"); + + return util::replace(id, "%", "0x25"); + }; + + return util::join(util::transform(util::split(std::move(id), "::"), map), "::"); +} + +cxx::Block::Block(std::vector stmts) { + _stmts.reserve(stmts.size()); + for ( auto& s : stmts ) + _stmts.emplace_back(std::move(s), Block(), 0); +} + +void cxx::Block::addStatement(std::string stmt) { _stmts.emplace_back(std::move(stmt), Block(), 0); } + +void cxx::Block::addStatementAtFront(std::string stmt) { _stmts.insert(_stmts.begin(), {std::move(stmt), Block(), 0}); } + +void cxx::Block::addBlock(Block child) { _stmts.emplace_back("", std::move(child), 0); } + +void cxx::Block::appendFromBlock(Block b) { + _stmts.insert(_stmts.end(), b._stmts.begin(), b._stmts.end()); + _tmps.insert(_tmps.end(), b._tmps.begin(), b._tmps.end()); +} + +void cxx::Block::addComment(const std::string& stmt, bool sep_before, bool sep_after) { + Flags f = 0; + + if ( sep_before ) + f |= flags::AddSeparatorBefore; + + if ( sep_after ) + f |= flags::AddSeparatorAfter; + + _stmts.emplace_back(fmt("// %s", stmt), Block(), f); +} + +inline std::string fmtDeclaration(const cxx::ID& id, const cxx::Type& type, const std::vector& args, + std::string linkage = "", std::optional init = {}) { + std::string sinit; + + if ( init ) + sinit = fmt(" = %s", *init); + + if ( ! linkage.empty() ) + linkage = fmt("%s ", linkage); + + std::string sargs; + if ( args.size() ) + sargs = fmt("(%s)", util::join(args, ", ")); + + return fmt("%s%s %s%s%s", linkage, type, id, sargs, sinit); +} + +void cxx::Block::addLocal(const declaration::Local& v) { + auto d = fmtDeclaration(v.id, v.type, v.args, v.linkage, v.init); + _stmts.emplace_back(std::move(d), Block(), 0); +} + +void cxx::Block::addTmp(const declaration::Local& v) { + auto d = fmtDeclaration(v.id, v.type, v.args, v.linkage, v.init); + _tmps.emplace_back(std::move(d)); +} + +void cxx::Block::addReturn(const Expression& expr) { _stmts.emplace_back(fmt("return %s", expr), Block(), 0); } + +void cxx::Block::addLambda(const std::string& name, const std::string& signature, cxx::Block body) { + body.setEnsureBracesforBlock(); + _stmts.emplace_back(fmt("auto %s = %s ", name, signature), body, flags::BlockEos); +} + +void cxx::Block::addIf(const Expression& cond, cxx::Block true_) { + true_._ensure_braces_for_block = true; + _stmts.emplace_back(fmt("if ( %s )", cond), true_, flags::AddSeparatorAfter); +} + +void cxx::Block::addIf(const Expression& init, const Expression& cond, cxx::Block true_) { + true_._ensure_braces_for_block = true; + _stmts.emplace_back(fmt("if ( %s; %s )", init, cond), true_, flags::AddSeparatorAfter); +} + +void cxx::Block::addIf(const Expression& cond, cxx::Block true_, cxx::Block false_) { + true_._ensure_braces_for_block = true; + false_._ensure_braces_for_block = true; + _stmts.emplace_back(fmt("if ( %s )", cond), true_, flags::NoSeparator); + _stmts.emplace_back("else", false_, flags::AddSeparatorAfter); +} + +void cxx::Block::addIf(const Expression& init, const Expression& cond, cxx::Block true_, cxx::Block false_) { + true_._ensure_braces_for_block = true; + false_._ensure_braces_for_block = true; + _stmts.emplace_back(fmt("if ( %s; %s )", init, cond), true_, flags::NoSeparator); + _stmts.emplace_back("else", false_, flags::AddSeparatorAfter); +} + +void cxx::Block::addElseIf(const Expression& cond, cxx::Block true_) { + true_._ensure_braces_for_block = true; + _stmts.emplace_back(fmt("else if ( %s )", cond), true_, flags::AddSeparatorAfter); +} + +void cxx::Block::addElse(cxx::Block true_) { + true_._ensure_braces_for_block = true; + _stmts.emplace_back("else ", true_, flags::AddSeparatorAfter); +} + +void cxx::Block::addWhile(const Expression& cond, const cxx::Block& body) { + _stmts.emplace_back(fmt("while ( %s )", cond), body, flags::AddSeparatorAfter); +} + +void cxx::Block::addFor(const Expression& init, const Expression& cond, const Expression& next, + const cxx::Block& body) { + _stmts.emplace_back(fmt("for ( %s; %s; %s )", init, cond, next), body, flags::AddSeparatorAfter); +} + +void cxx::Block::addForRange(bool const_, const ID& id, const Expression& seq, const cxx::Block& body) { + auto c = (const_ ? "const " : ""); + _stmts.emplace_back(fmt("for ( %sauto& %s : %s )", c, id, seq), body, flags::AddSeparatorAfter); +} + +#if 0 +void cxx::Block::addForRange(const Expression& init, bool const_, const ID& id, const Expression& seq, cxx::Block body) { + auto c = (const_ ? "const " : ""); + _stmts.emplace_back(fmt("for ( %s; %sauto& %s : %s )", init, c, id, seq), body, flags::AddSeparatorAfter); +} +#endif + +void cxx::Block::addSwitch(const Expression& cond, const std::vector>& cases_, + std::optional default_) { + auto x = Block(); + + for ( const auto& c : cases_ ) + x._stmts.emplace_back(fmt("case %s:", c.first), c.second, 0); + + if ( default_ ) + x._stmts.emplace_back("default:", *default_, 0); + + _stmts.emplace_back(fmt("switch ( %s )", cond), std::move(x), flags::AddSeparatorAfter); +} +void cxx::Block::addTry(Block body, std::vector> catches) { + body.setEnsureBracesforBlock(); + _stmts.emplace_back("try", std::move(body), flags::NoSeparator); + + for ( auto& [e, b] : catches ) { + b.setEnsureBracesforBlock(); + auto arg = std::string(e.type); + if ( e.id ) + arg = fmt("%s %s", arg, e.id); + + _stmts.emplace_back(fmt("catch ( %s )", arg), std::move(b), + (e == catches.back().first ? flags::AddSeparatorAfter : flags::NoSeparator)); + } +} + +size_t cxx::Block::size(bool ignore_comments) const { + auto x = 0; + for ( const auto& [s, b, f] : _stmts ) { + if ( ignore_comments && util::startsWith(s, "//") ) + continue; + + x += 1; + x += b.size(); + } + + return x; +} + +std::string cxx::declaration::Function::prototype(bool qualify) const { + std::string qualifier; + + if ( const_ ) + qualifier = " const"; + + if ( result == "void" || result == "auto" ) + return fmt("%s %s(%s)%s", result, (qualify ? id : id.local()), util::join(args, ", "), qualifier); + + if ( result == "" ) + return fmt("%s(%s)%s", (qualify ? id : id.local()), util::join(args, ", "), qualifier); + + return fmt("auto %s(%s)%s -> %s", (qualify ? id : id.local()), util::join(args, ", "), qualifier, result); +} + +std::string cxx::declaration::Function::parameters() const { return fmt("(%s)", util::join(args, ", ")); } + +cxx::Block& cxx::Block::operator+=(const cxx::Block& other) { + for ( const auto& s : other._stmts ) + _stmts.push_back(s); + + return *this; +} + +std::string cxx::declaration::Local::str() const { return fmtDeclaration(id, type, args, linkage, init); } + +std::string cxx::declaration::Global::str() const { return fmtDeclaration(id, type, args, linkage, init); } + +std::string cxx::type::Struct::str() const { + std::vector visitor_calls; + + auto fmt_member = [&](auto f) { + if ( auto x = std::get_if(&f) ) { + if ( ! (util::startsWith(x->id.local(), "__") || + x->linkage == "inline static") ) { // Don't visit internal or static fields. + visitor_calls.emplace_back(fmt("_(\"%s\", %s); ", x->id, x->id)); + } + + return fmt("%s;", x->str()); + } + + if ( auto x = std::get_if(&f) ) { + std::string linkage; + + if ( x->linkage == "static" ) + linkage = "static "; + + if ( x->linkage == "inline" ) + linkage = "inline "; + + if ( x->inline_body ) { + cxx::Formatter f; + f.compact_block = true; + f << (*x->inline_body); + return fmt("%s%s %s", linkage, x->prototype(false), util::trim(f.str())); + } + + return fmt("%s%s;", linkage, x->prototype(false)); + } + + throw std::bad_variant_access(); + }; + + auto fmt_argument = [&](auto a) { + if ( a.internal_type ) + return fmt("%s %s;", a.internal_type, a.id); + else + return fmt("%s %s;", a.type, a.id); + }; + + std::vector struct_fields; + util::append(struct_fields, util::transform(members, fmt_member)); + util::append(struct_fields, util::transform(args, fmt_argument)); + + if ( add_ctors ) { + auto dctor = fmt("inline %s() { __init(); }", type_name); + auto cctor = fmt("%s(const %s&) = default;", type_name, type_name); + auto mctor = fmt("%s(%s&&) = default;", type_name, type_name); + auto cassign = fmt("%s& operator=(const %s&) = default;", type_name, type_name); + auto massign = fmt("%s& operator=(%s&&) = default;", type_name, type_name); + + for ( auto x : {dctor, cctor, mctor, cassign, massign} ) + struct_fields.emplace_back(x); + + if ( args.size() ) { + // Add decidated constructor to initialize the struct's arguments. + auto params_ctor_args = + util::join(util::transform(args, [&](auto x) { return fmt("%s %s", x.type, x.id); }), ", "); + auto params_ctor = fmt("inline %s(%s);", type_name, params_ctor_args); + struct_fields.emplace_back(params_ctor); + } + } + + struct_fields.emplace_back( + fmt("template void __visit(F _) const { %s}", util::join(visitor_calls, ""))); + auto struct_fields_as_str = + util::join(util::transform(struct_fields, [&](auto x) { return fmt(" %s", x); }), "\n"); + + std::string has_params; + if ( args.size() ) + has_params = ", hilti::rt::trait::hasParameters"; + + return fmt("struct %s : hilti::rt::trait::isStruct%s, hilti::rt::Controllable<%s> {\n%s\n}", type_name, has_params, + type_name, struct_fields_as_str); +} + +std::string cxx::type::Struct::inlineCode() const { + if ( ! add_ctors ) + return ""; + + std::string params_ctor; + + if ( args.size() ) { + // Create constructor taking the struct's parameters. + auto params_ctor_args = + util::join(util::transform(args, [&](auto x) { return fmt("%s %s", x.type, x.id); }), ", "); + auto params_ctor_inits = + util::join(util::transform(args, [&](auto x) { return fmt("%s(std::move(%s))", x.id, x.id); }), ","); + + if ( params_ctor_inits.size() ) + params_ctor_inits = fmt(" : %s", params_ctor_inits); + + params_ctor = + fmt("inline %s::%s(%s)%s { __init(); }\n", type_name, type_name, params_ctor_args, params_ctor_inits); + } + + return params_ctor; +} + +std::string cxx::type::Union::str() const { + std::vector types; + std::vector visitor_calls; + + for ( const auto& [idx, member] : util::enumerate(members) ) { + auto decl = std::get(member); + types.emplace_back(decl.type); + visitor_calls.emplace_back(fmt("_(\"%s\", std::get_if<%d>(&this->value)); ", decl.id, idx + 1)); + } + + auto base = fmt("hilti::rt::Union<%s>", util::join(types, ", ")); + auto header = fmt(" using %s::Union;", base); + auto visit = fmt(" template void __visit(F _) const { %s}", util::join(visitor_calls, "")); + return fmt("struct %s : public %s {\n%s\n%s\n}", type_name, base, header, visit); +} + +std::string cxx::type::Enum::str() const { + // The following line triggers a NullDereference in tinyformat.h for some reason. + auto x = util::transform(labels, [](auto l) { + return fmt("%s = %d", l.first, l.second); + }); // NOLINT(clang-analyzer-core.NullDereference) + return fmt("enum class %s : int64_t { %s }", type_name, util::join(x, ", ")); +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::Block& x) { + auto braces = (f.ensure_braces_for_block || x.ensureBracesForBlock() || x._stmts.size() > 1 || + (x.size() == 1 && x.size(true) == 0)); + + if ( x._stmts.empty() && x._tmps.empty() && ! braces ) + return f; + + auto compact_block = f.compact_block; + auto eos_after_block = f.eos_after_block; + auto ensure_braces_for_block = f.ensure_braces_for_block; + auto sep_after_block = f.sep_after_block; + + f.ensure_braces_for_block = false; + f.compact_block = false; + f.eos_after_block = false; + f.sep_after_block = true; + + if ( braces && compact_block ) + f << "{ "; + + if ( braces && ! compact_block ) + f << '{' << indent() << eol(); + + if ( ! braces && ! compact_block ) + f << indent() << eol(); + + if ( ! x._stmts.empty() || ! x._tmps.empty() ) { + for ( const auto& t : x._tmps ) + f << t << ";"; + + if ( ! x._tmps.empty() ) + f << separator(); + + for ( auto [i, y] : util::enumerate(x._stmts) ) { + auto [s, b, fl] = y; + + if ( fl & flags::AddSeparatorBefore && i != 0 ) + f << separator(); + + if ( fl & flags::BlockEos ) { + f << s; + f.eos_after_block = true; + f << b; + } + else { + if ( compact_block && ! b ) + f << s << ';'; + else if ( ! b ) + f << s << eos() << b; + else { + if ( ! s.empty() ) + f << s << ' '; + + f.sep_after_block = ! (fl & flags::NoSeparator); + + if ( s.empty() ) + f << separator(); + + f << b; + + if ( s.empty() ) + f << separator(); + } + } + + if ( fl & flags::AddSeparatorAfter && i != (x._stmts.size() - 1) ) + f << separator(); + } + } + + if ( braces && compact_block ) { + if ( eos_after_block ) + f << " }" << eos(); + else + f << " }" << eol(); + } + + if ( braces && ! compact_block ) { + f << dedent(); + if ( eos_after_block ) + f << '}' << eos() << separator(); + else { + f << '}' << eol(); + + if ( ensure_braces_for_block && sep_after_block ) + f << separator(); + } + } + + if ( ! braces && ! compact_block ) + f << dedent(); + + return f; +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::Expression& x) { return f << std::string(x); } + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::ID& x) { + if ( x.namespace_() == f.namespace_() ) + return f << x.local().str(); + + return f << x.str(); +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::Type& x) { + return f << util::replace(x, fmt("%s::", *f.namespace_(0)), ""); +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::declaration::Type& x) { + auto id = x.id.local(); + + if ( x.id.namespace_() ) + id = cxx::ID(x.id.namespace_(), id); + + f.enterNamespace(id.namespace_()); + + if ( ! x.no_using && id.local() ) + f << fmt("using %s = ", id.local()) << x.type << eos(); + else + f << x.type << eos(); + + if ( x.type.isMultiLine() ) + f << eol(); + + return f; +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::declaration::IncludeFile& x) { + return f << fmt("#include <%s>", x.file) << eol(); +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::declaration::Local& x) { + f << x.type << ' ' << x.id.local(); + + if ( x.init ) + f << " = " << *x.init; + + f << eos(); + + return f; +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::declaration::Global& x) { + f.enterNamespace(x.id.namespace_()); + + if ( x.linkage ) + f << x.linkage << ' '; + + f << x.type << ' ' << x.id.local(); + + if ( x.init ) + f << " = " << *x.init; + + f << eos(); + + return f; +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::declaration::Function& x) { + f.enterNamespace(x.id.namespace_()); + + if ( x.attribute ) + f << x.attribute << ' '; + + if ( x.linkage ) + f << x.linkage << ' '; + + if ( x.inline_body ) + f << "inline "; + + f << x.prototype(false); + + if ( x.inline_body ) { + f.ensure_braces_for_block = true; + f << ' ' << *x.inline_body; + } + else + f << eos(); + + return f; +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::Function& x) { + if ( x.declaration.attribute ) + f << x.declaration.attribute << ' '; + + if ( x.declaration.linkage ) + f << x.declaration.linkage << ' '; + + f << x.declaration.prototype(true); + + if ( x.default_ ) + f << " = default;" << eol(); + else { + f.ensure_braces_for_block = true; + f.compact_block = (x.body.size() <= 1); + f << ' ' << x.body; + } + + return f; +} + +cxx::Formatter& cxx::operator<<(cxx::Formatter& f, const cxx::declaration::Constant& x) { + f.enterNamespace(x.id.namespace_()); + + if ( x.linkage ) + f << x.linkage << ' '; + + f << "const " << x.type << ' ' << x.id.local(); + + if ( x.init ) + f << " = " << *x.init; + + f << eos(); + + return f; +} + +void cxx::to_json(json& j, const cxx::ID& id) { j = std::string(id); } + +void cxx::from_json(const json& j, cxx::ID& id) { id = cxx::ID(j); } + +void cxx::declaration::to_json(json& j, const cxx::declaration::Argument& a) { + j = json{{"id", a.id}, {"type", a.type}}; +} + +void cxx::declaration::from_json(const json& j, cxx::declaration::Argument& a) { + a.id = cxx::ID::fromNormalized(j.at("id").get()); + a.type = j.at("type").get(); +} + +void cxx::declaration::to_json(json& j, const cxx::declaration::Constant& c) { + j = json{{"id", c.id}, {"type", c.type}, {"init", c.init ? *c.init : ""}, {"linkage", c.linkage}}; +} + +void cxx::declaration::from_json(const json& j, cxx::declaration::Constant& c) { + c.id = j.at("id").get(); + c.type = j.at("type").get(); + c.init = j.at("init").get(); + c.linkage = j.at("linkage").get(); +} + +void cxx::declaration::to_json(json& j, const cxx::declaration::Type& t) { + j = json{{"id", t.id}, + {"type", t.type}, + {"forward_decl", t.forward_decl}, + {"forward_decl_prio", t.forward_decl_prio}}; +} + +void cxx::declaration::from_json(const json& j, cxx::declaration::Type& t) { + t.id = j.at("id").get(); + t.type = j.at("type").get(); + t.forward_decl = j.at("forward_decl").get(); + t.forward_decl_prio = j.at("forward_decl_prio").get(); +} + +void cxx::declaration::to_json(json& j, const cxx::declaration::Function& f) { + j = json{{"result", f.result}, {"id", f.id}, {"args", f.args}, {"linkage", f.linkage}, {"attribute", f.attribute}}; +} + +void cxx::declaration::from_json(const json& j, cxx::declaration::Function& f) { + f.result = j.at("result").get(); + f.id = j.at("id").get(); + f.args = j.at("args").get>(); + f.linkage = j.at("linkage").get(); + f.attribute = j.at("attribute").get(); +} diff --git a/hilti/src/compiler/cxx/formatter.cc b/hilti/src/compiler/cxx/formatter.cc new file mode 100644 index 000000000..a33750879 --- /dev/null +++ b/hilti/src/compiler/cxx/formatter.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include + +using namespace hilti::detail::cxx; +using namespace hilti::detail::cxx::formatter; + +void Formatter::pushNamespace(const std::string& relative_ns) { + auto& f = *this; + f.separator(); + + if ( ! relative_ns.empty() ) { + f << "namespace " << relative_ns << " {"; + f.indent(); + f.eol(); + } + + _namespaces.push_back(relative_ns); +} + +void Formatter::enterNamespace(const std::string& absolute_ns) { + while ( ! _namespaces.empty() ) { + auto current = util::split(util::join(_namespaces, "::"), "::"); + auto target = util::split(absolute_ns, "::"); + + unsigned int i = 0; + while ( i < std::min(target.size(), current.size()) && target[i] == current[i] ) { + i++; + } + + if ( i == target.size() && i == current.size() ) + // No change. + return; + + if ( i >= current.size() ) { + pushNamespace(util::join(util::slice(target, i), "::")); + return; + } + + popNamespace(); + } + + pushNamespace(absolute_ns); +} + +void Formatter::popNamespace() { + assert(_namespaces.size()); + + auto& f = *this; + + if ( ! _namespaces.back().empty() ) { + f.dedent(); + f << '}'; + // f << "} // namespace " << _namespaces.back(); + f.eol(); + } + + f.separator(); + _namespaces.pop_back(); +} + +std::optional Formatter::namespace_(int level) const { + if ( ! _namespaces.empty() ) + return util::join(util::slice(_namespaces, level), "::"); + + return {}; +} + +void Formatter::leaveNamespace() { + while ( ! _namespaces.empty() ) + popNamespace(); +} + +hilti::detail::cxx::ID Formatter::relativeID(const cxx::ID& id, int level) const { + auto ns = cxx::ID{util::join(util::slice(_namespaces, level - 1), "::")}; + return id.relativeTo(ns); +} diff --git a/hilti/src/compiler/cxx/linker.cc b/hilti/src/compiler/cxx/linker.cc new file mode 100644 index 000000000..99784a381 --- /dev/null +++ b/hilti/src/compiler/cxx/linker.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using nlohmann::json; + +using namespace hilti; +using namespace hilti::detail; +using namespace hilti::detail::cxx::formatter; +using util::fmt; + +namespace hilti::logging::debug { +inline const DebugStream Compiler("compiler"); +} // namespace hilti::logging::debug + +void cxx::Linker::add(const linker::MetaData& md) { + auto id = md.at("module").get(); + auto path = md.at("path").get(); + auto ns = md.at("namespace").get(); + _modules.emplace(id, path); + + // Continues logging from CodeGen::linkUnits. + HILTI_DEBUG(logging::debug::Compiler, fmt(" - module %s (%s)", id, path)); + + for ( const auto& j : md.value("joins", json::object_t()) ) { + for ( auto& s : j.second ) { + auto& joins = _joins[j.first]; + joins.push_back(s.get()); + } + } + + if ( auto idx = md.value("globals-index", cxx::declaration::Constant()); ! idx.id.empty() ) + _globals.insert(std::move(idx)); +} + +void cxx::Linker::finalize() { + if ( _modules.empty() ) { + _linker_unit = cxx::Unit(_codegen->context(), "__linker__", ""); + return; + } + + cxx::Unit unit(_codegen->context(), "__linker__"); + unit.addComment("Linker code generated for modules:"); + + for ( const auto& m : _modules ) + unit.addComment(fmt(" - %s (%s)", m.first, m.second)); + + unit.add(cxx::declaration::IncludeFile{"hilti/rt/libhilti.h"}); + + std::string init_modules = "nullptr"; + std::string init_globals = "nullptr"; + + for ( const auto& j : _joins ) { + for ( const auto& c : j.second ) { + if ( ! c.declare_only ) + unit.add(c.callee); + + for ( const auto& t : c.aux_types ) + unit.add(t); + } + } + + for ( const auto& j : _joins ) { + auto impl = cxx::Function(); + auto body = cxx::Block(); + + bool first = true; + for ( const auto& c : j.second ) { + if ( first ) { + impl.declaration = c.callee; + impl.declaration.id = c.id; + first = false; + } + + if ( c.declare_only ) + continue; + + auto args = util::transform(impl.declaration.args, [](auto& a) { return a.id; }); + + if ( std::string(c.callee.result) != "void" ) { + cxx::Block done_body; + done_body.addStatement("return x;"); + impl.body.addIf(fmt("auto x = %s(%s)", c.callee.id, util::join(args, ", ")), std::move(done_body)); + } + else + impl.body.addStatement(fmt("%s(%s)", c.callee.id, util::join(args, ", "))); + } + + if ( std::string(impl.declaration.result) != "void" ) + impl.body.addStatement("return {}"); + + unit.add(impl.declaration); + unit.add(impl); + } + + unsigned int cnt = 0; + for ( auto g : _globals ) { + g.init = fmt("%u", cnt++); + g.linkage = "extern"; + unit.add(g); + } + + unit.finalize(); + _linker_unit = std::move(unit); +} + +Result cxx::Linker::linkerUnit() { + if ( _linker_unit ) + return *_linker_unit; + + return result::Error("linked unit has not been finalized"); +} diff --git a/hilti/src/compiler/cxx/unit.cc b/hilti/src/compiler/cxx/unit.cc new file mode 100644 index 000000000..db099da96 --- /dev/null +++ b/hilti/src/compiler/cxx/unit.cc @@ -0,0 +1,473 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace hilti::detail::cxx; +using namespace hilti::detail::cxx::formatter; +using nlohmann::json; +using util::fmt; + +Unit::Unit(std::shared_ptr context) : _context(std::move(context)) {} + +Unit::Unit(std::shared_ptr context, cxx::ID module_id, const std::string& cxx_code) + : _context(std::move(context)), _module_id(std::move(module_id)), _no_linker_meta_data(true), _cxx_code(cxx_code) {} + +Unit::Unit(std::shared_ptr context, cxx::ID module_id) + : _context(std::move(context)), _module_id(std::move(module_id)), _no_linker_meta_data(true) {} + +void Unit::setModule(const hilti::Module& m) { + _module_id = cxx::ID(m.id()); + _module_path = m.meta().location().file(); +} + +void Unit::add(const declaration::IncludeFile& i, const Meta& m) { _includes.insert(i); } + +void Unit::add(const declaration::Global& g, const Meta& m) { + if ( auto i = _globals.find(g.id); i != _globals.end() ) { + if ( i->second == g ) + return; + + logger().internalError(fmt("global '%s' already exists differnently in C++ translation unitn", g.id), + m.location()); + } + + _globals.emplace(g.id, g); + _ids.insert(g.id); + + if ( g.id.namespace_() ) + _namespaces.insert(g.id.namespace_()); +} + +void Unit::add(const declaration::Constant& c, const Meta& m) { + if ( auto i = _constants.find(c.id); i != _constants.end() ) { + if ( i->second == c ) + return; + + logger().internalError(fmt("constant '%s' already exists differnently in C++ translation unitn", c.id), + m.location()); + } + + _constants.emplace(c.id, c); + _ids.insert(c.id); + + if ( c.id.namespace_() ) + _namespaces.insert(c.id.namespace_()); +} + +void Unit::add(const declaration::Type& t, const Meta& m) { + if ( t.forward_decl ) + _types_forward.insert_or_assign(t.id, t); + + else { + if ( auto i = _types.find(t.id); i != _types.end() ) { + if ( i->second == t ) + return; + + logger().internalError(fmt("type '%s' already exists with different definition in C++ translation unit", + t.id), + m.location()); + } + + _types.insert_or_assign(t.id, t); + } + + _ids.insert(t.id); + + if ( t.id.namespace_() ) + _namespaces.insert(t.id.namespace_()); +} + +std::optional Unit::lookupType(const ID& id) const { + if ( auto t = _types.find(id); t != _types.end() ) + return t->second; + + return {}; +} + +void Unit::add(const declaration::Function& f, const Meta& m) { + auto current = _function_declarations.equal_range(f.id); + + for ( auto i = current.first; i != current.second; i++ ) { + if ( i->second == f ) + return; + } + + _function_declarations.emplace(f.id, f); + _ids.insert(f.id); + + if ( f.id.namespace_() ) + _namespaces.insert(f.id.namespace_()); +} + +void Unit::add(const Function& f, const Meta& m) { + auto current = _function_implementations.equal_range(f.declaration.id); + + for ( auto i = current.first; i != current.second; i++ ) { + if ( i->second == f ) + return; + } + + _function_implementations.emplace(f.declaration.id, f); +} + +void Unit::add(const std::string& stmt, const Meta& m) { _statements.emplace_back(stmt); } + +void Unit::add(const linker::Join& f) { + auto d = f.callee; + d.id = f.id; + d.linkage = "extern"; + add(d); + + _linker_joins.insert(f); +} + +void Unit::addComment(const std::string& comment) { _comments.push_back(comment); } + +bool Unit::hasDeclarationFor(const cxx::ID& id) { return _ids.find(id) != _ids.end(); } + +void Unit::_addHeader(Formatter& f) { + auto c = fmt("of %s", _module_id); + if ( _module_path != "" ) + c += fmt(" (from %s)", _module_path); + + f << separator() << comment(fmt("Begin %s", c)) + << comment(fmt("Compiled by HILTI version %s", hilti::configuration().version_string)) << separator() + << declaration::IncludeFile{.file = "hilti/rt/compiler-setup.h"} << separator(); +} + +void Unit::_addModuleInitFunction() { + auto addInitFunction = [&](Context* ctx, auto f, std::string id_) { + auto id = cxx::ID{cxxNamespace(), std::move(id_)}; + + auto body_decl = cxx::declaration::Function{.result = "void", .id = id, .args = {}, .linkage = "extern"}; + + cxx::Block body; + body.appendFromBlock(std::move(f)); + + auto body_impl = cxx::Function{.declaration = body_decl, .body = std::move(body)}; + + add(body_decl); + add(body_impl); + return id; + }; + + if ( _init_globals ) + addInitFunction(_context.get(), _init_globals, "__init_globals"); + + if ( _init_module ) + addInitFunction(_context.get(), _init_module, "__init_module"); + + if ( moduleID() != cxx::ID("__linker__") ) { + cxx::Block register_; + register_.addStatement(fmt("hilti::rt::detail::registerModule({ \"%s\", %s, %s, %s})", moduleID(), + _init_module ? "&__init_module" : "nullptr", + _uses_globals ? "&__init_globals" : "nullptr", + _uses_globals ? "&__globals_index" : "nullptr")); + + auto id = addInitFunction(_context.get(), register_, "__register_module"); + add(fmt("HILTI_PRE_INIT(%s)", id)); + } +} + +hilti::Result Unit::finalize() { + if ( ! _module_id ) + return result::Error("no module set"); + + _addModuleInitFunction(); + + auto f = Formatter(); + + _addHeader(f); + + _namespaces.insert(""); + + if ( ! _comments.empty() ) { + f << comment(""); + + for ( const auto& c : _comments ) + f << comment(c); + + f << separator(); + } + + for ( const auto& i : _includes ) + f << i; + + f << separator(); + + for ( const auto& ns : _namespaces ) { + for ( const auto& i : _types_forward ) { + if ( i.second.id.namespace_() == ns && i.second.forward_decl && i.second.forward_decl_prio ) + f << i.second; + } + } + + for ( const auto& ns : _namespaces ) { + for ( const auto& i : _types_forward ) { + if ( i.second.id.namespace_() == ns && i.second.forward_decl && ! i.second.forward_decl_prio ) + f << i.second; + } + } + + for ( const auto& ns : _namespaces ) { + std::unordered_set done; + + // Write out those types first that we have in _types_in_order. + for ( const auto& id : _types_in_order ) { + auto i = _types.find(id); + assert(i != _types.end()); + auto& t = i->second; + if ( t.id.namespace_() == ns && ! t.forward_decl ) + f << t; + + done.insert(std::string(id)); + } + + // Now write the remaining types. + for ( const auto& t : _types ) { + if ( done.find(t.first) != done.end() ) + continue; + + if ( t.second.id.namespace_() == ns && ! t.second.forward_decl ) + f << t.second; + } + + for ( const auto& i : _constants ) { + if ( i.second.id.namespace_() == ns ) + f << i.second; + } + + for ( const auto& i : _globals ) { + if ( i.second.id.namespace_() == ns ) + f << i.second; + } + + for ( const auto& i : _function_declarations ) { + if ( i.second.id.namespace_() != ns ) + continue; + + auto seperator = (i.second.inline_body && i.second.inline_body->size() > 1); + + if ( seperator ) + f << separator(); + + f << i.second; + + if ( seperator ) + f << separator(); + } + } + + f.leaveNamespace(); + + // Add any inline code that types may have defined. + for ( const auto& ns : _namespaces ) { + for ( const auto& t : _types ) { + if ( t.second.id.namespace_() == ns && t.second.inline_code.size() ) { + f.enterNamespace(t.second.id.namespace_()); + f << t.second.inline_code << eol(); + } + } + } + + f.leaveNamespace(); + + for ( const auto& s : _statements ) + f.printString(s + "\n"); + + if ( _statements.size() ) + f << separator(); + + for ( const auto& i : _function_implementations ) + f << separator() << i.second; + + if ( auto meta = linkerMetaData() ) { + f << separator(); + f << "/* __HILTI_LINKER_V1__" << eol(); + f << meta->dump(-1) << eol(); + f << "*/" << eol() << separator(); + } + + _cxx_code = f.str(); + return Nothing(); +} + +hilti::Result Unit::print(std::ostream& out) const { + if ( ! _cxx_code ) + return result::Error("unit does not have any C++ code to print"); + + out << *_cxx_code; + return Nothing(); +} + +hilti::Result Unit::createPrototypes(std::ostream& out) { + if ( ! (_module_id && _cxx_code) ) + return result::Error("cannot generate prototypes for module"); + + auto f = Formatter(); + + f << separator(); + f << comment(fmt("Prototypes for module %s", _module_id)); + f << separator(); + f << fmt("#ifndef HILTI_PROTOTYPES_%s_H", util::toupper(_module_id)) << eol(); + f << separator(); + + for ( const auto& i : _includes ) + f << i; + + f << separator(); + + for ( const auto& ns : _namespaces ) { + for ( const auto& i : _types ) { + // Print all types, a function prototype might need one. + if ( i.second.id.namespace_() == ns ) + f << i.second; + } + + for ( auto i : _function_declarations ) { + if ( i.second.id.namespace_() != ns ) + continue; + + if ( std::string(i.second.linkage).find("extern") == std::string::npos && + std::string(i.second.linkage).find("inline") == std::string::npos ) + continue; + + auto seperator = (i.second.inline_body && i.second.inline_body->size() > 1); + + if ( seperator ) + f << separator(); + + f << i.second; + + if ( seperator ) + f << separator(); + } + } + + f.leaveNamespace(); + + f << "#endif" << eol(); + + out << f.str(); + return Nothing(); +} + +void Unit::importDeclarations(const Unit& other) { + const auto m = Meta(Location("")); + + for ( const auto& i : other._constants ) + add(i.second, m); + + for ( const auto& i : other._types ) + add(i.second, m); + + for ( const auto& i : other._types_forward ) + add(i.second, m); + + for ( const auto& i : other._types ) + add(i.second, m); + + for ( const auto& i : other._function_declarations ) { + if ( std::string(i.second.linkage).find("extern") == std::string::npos && + std::string(i.second.linkage).find("inline") == std::string::npos ) + continue; + + add(i.second, m); + } + + for ( const auto& i : other._function_implementations ) { + if ( std::string(i.second.declaration.linkage).find("inline") == std::string::npos ) + continue; + + add(i.second, m); + } + + for ( const auto& i : other._includes ) + add(i, m); +} + +hilti::detail::cxx::ID Unit::cxxNamespace() const { + return cxx::ID(_context->options().cxx_namespace_intern, moduleID()); +} + +hilti::Result Unit::linkerMetaData() const { + if ( _no_linker_meta_data ) + return result::Error("module does not have meta data"); + + auto joins = json::object(); + + for ( const auto& f : _linker_joins ) + joins[f.id].push_back(f); + + json j; + j["version"] = 1; + j["module"] = _module_id; + j["path"] = util::normalizePath(_module_path); + j["namespace"] = cxxNamespace(); + + if ( ! joins.empty() ) + j["joins"] = joins; + + return j; +} + +std::pair> Unit::readLinkerMetaData(std::istream& input) { + std::string line; + + bool in_md = false; + std::string data; + + while ( std::getline(input, line) ) { + if ( in_md ) { + if ( util::startsWith(util::trim(line), "*/") ) + in_md = false; + } + + if ( in_md ) + data += line; + + if ( ! in_md ) { + if ( util::trim(line) == "/* __HILTI_LINKER_V1__" ) + in_md = true; + } + } + + if ( input.bad() ) + return std::make_pair(false, std::nullopt); + + if ( data.empty() ) + return std::make_pair(true, std::nullopt); + + try { + auto md = nlohmann::json::parse(data); + return std::make_pair(true, md); + } catch ( nlohmann::json::parse_error& e ) { + return std::make_pair(false, std::nullopt); + } +} + +void linker::to_json(nlohmann::json& j, const linker::Join& x) { + j = json{ + {"id", x.id}, + {"callee", x.callee}, + {"aux_types", x.aux_types}, + {"priority", x.priority}, + {"declare_only", x.declare_only}, + }; +} + +void linker::from_json(const nlohmann::json& j, linker::Join& x) { + x.id = j.at("id").get(); + x.callee = j.at("callee").get(); + x.aux_types = j.at("aux_types").get>(); + x.priority = j.at("priority").get(); + x.declare_only = j.at("declare_only").get(); +} diff --git a/hilti/src/compiler/driver.cc b/hilti/src/compiler/driver.cc new file mode 100644 index 000000000..d5de21714 --- /dev/null +++ b/hilti/src/compiler/driver.cc @@ -0,0 +1,908 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include + +#include +#include + +using namespace hilti; +using util::fmt; + +namespace hilti::logging::debug { +inline const DebugStream Compiler("compiler"); +inline const DebugStream Driver("driver"); +} // namespace hilti::logging::debug + +static struct option long_driver_options[] = {{"abort-on-exceptions", required_argument, nullptr, 'A'}, + {"show-backtraces", required_argument, nullptr, 'B'}, + {"compiler-debug", required_argument, nullptr, 'D'}, + {"debug", no_argument, nullptr, 'd'}, + {"debug-addl", required_argument, nullptr, 'X'}, + {"dump-code", no_argument, nullptr, 'C'}, + {"help", no_argument, nullptr, 'h'}, + {"include-linker", no_argument, nullptr, 'K'}, + {"keep-tmps", no_argument, nullptr, 'T'}, + {"library-path", required_argument, nullptr, 'L'}, + {"optimize", no_argument, nullptr, 'O'}, + {"output", required_argument, nullptr, 'o'}, + {"output-c++", no_argument, nullptr, 'c'}, + {"output-hilti", no_argument, nullptr, 'p'}, + {"disable-jit", no_argument, nullptr, 'J'}, + {"execute-code", no_argument, nullptr, 'j'}, + {"output-linker", no_argument, nullptr, 'l'}, + {"output-prototypes", no_argument, nullptr, 'P'}, + {"output-all-dependencies", no_argument, nullptr, 'e'}, + {"output-code-dependencies", no_argument, nullptr, 'E'}, + {"report-times", required_argument, nullptr, 'R'}, + {"skip-validation", no_argument, nullptr, 'V'}, + {"skip-dependencies", no_argument, nullptr, 'S'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +Driver::Driver(std::string name, const std::string_view& argv0) : _name(std::move(name)) { + if ( argv0.size() ) + configuration().initLocation(argv0); +} + +Driver::~Driver() { + if ( ! _driver_options.keep_tmps ) { + for ( const auto& t : _tmp_files ) + unlink(t.c_str()); + } +} + +void Driver::usage() { + auto exts = util::join(plugin::registry().supportedExtensions(), ", "); + + std::cerr + << "Usage: " << _name + << " [options] \n" + "\n" + "Options controlling code generation:\n" + "\n" + " -A | --abort-on-exceptions When executing compiled code, abort() instead of throwing HILTI " + "exceptions.\n" + " -B | --show-backtraces Include backtraces when reporting unhandled exceptions.\n" + " -C | --dump-code Dump all generated code to disk for debugging.\n" + " -D | --compiler-debug Activate compile-time debugging output for given debug streams " + "(comma-separated; 'help' for list).\n" + " -e | --output-all-dependencies Output list of dependencies for all compiled modules.\n" + " -E | --output-code-dependencies Output list of dependencies for all compiled modules that require " + "separate compilation of their own.\n" + " -K | --include-linker With --output-c++, include HILTI linker glue code.\n" + " -L | --library-path Add path to list of directories to search when importing modules.\n" + " -O | --optimize Build optimized release version of generated code.\n" + " -P | --output-prototypes Output C++ header with prototypes for public functionality.\n" + " -V | --skip-validation Don't validate ASTs (for debugging only).\n" + " -c | --output-c++ Print out all generated C++ code (including linker glue by default).\n" + " -d | --debug Include debug instrumentation into generated code.\n" +#ifdef HILTI_HAVE_JIT + // Don't show this if we don't have JIT. We still accept the + // option, but issue an error an runtime. + " -j | --jit-code Fully compile all code, and then execute it unless --output-to gives a " + "file to store it\n" +#endif + " -l | --output-linker Print out only generated HILTI linker glue code.\n" + " -o | --output-to Path for saving output.\n" + " -p | --output-hilti Just output parsed HILTI code again.\n" + " -R | --report-times Report a break-down of compiler's execution time.\n" + " -S | --skip-dependencies Do not automatically compile dependencies during JIT.\n" + " -T | --keep-tmps Do not delete any temporary files created.\n" + " -v | --version Print version information.\n" + " -X | --debug-addl Implies -d and adds selected additional instrumentation " + "(comma-separated; see 'help' for list).\n" + "\n" + "Inputs can be " + << exts + << ", .cc/.cxx, *.hlto.\n" + "\n"; +} + +result::Error Driver::error(std::string_view msg, const std::filesystem::path& p) { + auto x = fmt("%s: %s", _name, msg); + + if ( ! p.empty() ) + x += fmt(" (%s)", p.native()); + + return result::Error(std::move(x)); +} + +result::Error Driver::augmentError(const result::Error& err, const std::filesystem::path& p) { + return error(err.description(), p); +} + +Result Driver::openOutput(const std::filesystem::path& p, bool binary) { + auto mode = std::ios::out | std::ios::trunc; + + if ( p == "/dev/stdout" || p == "/dev/stderr" ) + mode = std::ios::out | std::ios::app; + + if ( binary ) + mode |= std::ios::binary; + + std::ofstream out(p, mode); + + if ( ! out.is_open() ) + return error("Cannot open file for output", p); + + return {std::move(out)}; +} + +Result Driver::openInput(std::ifstream& in, const std::filesystem::path& p) { + in.open(p); + + if ( ! in.is_open() ) + return error("Cannot open file for reading", p); + + return Nothing(); +} + +Result Driver::readInput(const std::filesystem::path& p) { + std::ifstream in; + if ( auto x = openInput(in, p); ! x ) + return x.error(); + + std::stringstream out; + + if ( ! util::copyStream(in, out) ) + return error("Error reading from file", p); + + return std::move(out); +} + +Result Driver::writeOutput(std::ifstream& in, const std::filesystem::path& p) { + auto out = openOutput(p); + + if ( ! out ) + return out.error(); + + if ( ! util::copyStream(in, *out) ) + return error("Error writing to file", p); + + return Nothing(); +} + +Result Driver::writeToTemp(std::ifstream& in, const std::string& name_hint, + const std::string& extension) { + auto template_ = fmt("%s.XXXXXX.%s", name_hint, extension); + char name[template_.size() + 1]; + strcpy(name, template_.c_str()); // NOLINT + auto fd = mkstemp(name); + + if ( fd < 0 ) + return error("Cannot open temporary file"); + + // Not sure if this is safe, but it seems to be what everybody does ... + std::ofstream out(name); + close(fd); + + if ( ! util::copyStream(in, out) ) + return error("Error writing to file", std::string(name)); + + _tmp_files.insert(name); + return std::filesystem::path(name); +} + +void Driver::dumpUnit(const Unit& unit) { + if ( unit.isCompiledHILTI() ) { + auto output_path = util::fmt("dbg.%s.hlt", unit.id()); + if ( auto out = openOutput(output_path) ) { + HILTI_DEBUG(logging::debug::Driver, fmt("saving HILTI code for module %s to %s", unit.id(), output_path)); + unit.print(*out); + } + } + + if ( auto cxx = unit.cxxCode() ) { + ID id = (unit.isCompiledHILTI() ? unit.id() : ID(unit.cxxCode()->id())); + auto output_path = util::fmt("dbg.%s.cc", id); + if ( auto out = openOutput(util::fmt("dbg.%s.cc", id)) ) { + HILTI_DEBUG(logging::debug::Driver, fmt("saving C++ code for module %s to %s", id, output_path)); + cxx->save(*out); + } + } +} + +Result Driver::parseOptions(int argc, char** argv) { + int num_output_types = 0; + + while ( true ) { + int c = getopt_long(argc, argv, "ABlKL:OcCpPvjJhvVdX:o:D:TEeSR", long_driver_options, nullptr); + + if ( c < 0 ) + break; + + switch ( c ) { + case 'A': _driver_options.abort_on_exceptions = true; break; + + case 'B': _driver_options.show_backtraces = true; break; + + case 'c': + _driver_options.output_cxx = true; + ++num_output_types; + break; + + case 'C': { + _driver_options.dump_code = true; + break; + } + + case 'd': { + _compiler_options.debug = true; + break; + } + + case 'X': { + auto arg = std::string(optarg); + + if ( arg == "help" ) { + std::cerr << "Additional debug instrumentation:\n"; + std::cerr << " flow: log function calls to debug stream \"hilti-flow\"\n"; + std::cerr << " location: track current source code location for error reporting\n"; + std::cerr << " trace: log statements to debug stream \"hilti-trace\"\n"; + std::cerr << "\n"; + exit(0); + } + + _compiler_options.debug = true; + + if ( auto r = _compiler_options.parseDebugAddl(arg); ! r ) + error(r.error()); + + break; + } + + case 'D': { + auto arg = std::string(optarg); + + if ( arg == "help" ) { + std::cerr << "Debug streams:\n"; + + for ( const auto& s : logging::DebugStream::all() ) + std::cerr << " " << s << "\n"; + + std::cerr << "\n"; + exit(0); + } + + for ( const auto& s : util::split(arg, ",") ) { + if ( ! _driver_options.logger->debugEnable(s) ) + return error(fmt("Unknown debug stream '%s', use 'help' for list", arg)); + } + + break; + } + + case 'e': + _driver_options.output_dependencies = driver::Dependencies::All; + ++num_output_types; + break; + + case 'E': + _driver_options.output_dependencies = driver::Dependencies::Code; + ++num_output_types; + break; + + case 'J': _driver_options.disable_jit = true; break; + + case 'j': + _driver_options.execute_code = true; + _driver_options.disable_jit = false; + _driver_options.include_linker = true; + ++num_output_types; + break; + + case 'l': + _driver_options.output_linker = true; + _driver_options.include_linker = true; + ++num_output_types; + break; + + case 'K': _driver_options.include_linker = true; break; + + case 'L': _compiler_options.library_paths.emplace_back(std::string(optarg)); break; + + case 'o': _driver_options.output_path = std::string(optarg); break; + + case 'O': _compiler_options.optimize = true; break; + + case 'p': + _driver_options.output_hilti = true; + ++num_output_types; + break; + + case 'P': + _driver_options.output_prototypes = true; + ++num_output_types; + break; + + case 'R': _driver_options.report_times = true; break; + + case 'S': _driver_options.skip_dependencies = true; break; + + case 'T': _driver_options.keep_tmps = true; break; + + case 'v': + std::cerr << _name << " v" << hilti::configuration().version_string_long << std::endl; + return Nothing(); + + case 'V': _compiler_options.skip_validation = true; break; + + case 'h': usage(); return Nothing(); + + default: usage(); return error(fmt("option %c not implemented", c)); + } + } + + while ( optind < argc ) + _driver_options.inputs.emplace_back(argv[optind++]); + + if ( _driver_options.inputs.empty() ) + return error("no input file given"); + + if ( num_output_types > 1 ) + return error("only one type of output can be specificied"); + + if ( num_output_types == 0 ) + return error("no output type given"); + + if ( ! _compiler_options.debug ) { + if ( _compiler_options.debug_trace || _compiler_options.debug_flow ) + return error("cannot use --optimize with --cgdebug"); + } + + if ( _driver_options.execute_code and ! _driver_options.output_path.empty() ) { + if ( ! util::endsWith(_driver_options.output_path, ".hlto") ) + return error("destination path for object code must have '.hlto' extension"); + } + + return Nothing(); +} + +Result Driver::initialize() { + if ( _stage != Stage::UNINITIALIZED ) + logger().internalError("unexpected driver stage in initialize()"); + + _stage = INITIALIZED; + + if ( _driver_options.logger ) + setLogger(std::move(_driver_options.logger)); + + _ctx = std::make_shared(_compiler_options); + return Nothing(); +} + +void Driver::setCompilerOptions(hilti::Options options) { + if ( _stage != Stage::UNINITIALIZED ) + logger().internalError("setCompilerOptions() must be called before initialization"); + + _compiler_options = std::move(options); +} + +void Driver::setDriverOptions(driver::Options options) { + if ( _stage != Stage::UNINITIALIZED ) + logger().internalError("setCompilerOptions() must be called before initialization"); + + _driver_options = std::move(options); +} + +void Driver::_addUnit(Unit unit) { + if ( _processed_units.find(unit.id()) != _processed_units.end() ) + return; + + if ( (! unit.path().empty()) && _processed_paths.find(unit.path()) != _processed_paths.end() ) + return; + + _processed_units.insert(unit.id()); + + if ( (! unit.path().empty()) ) + _processed_paths.insert(unit.path()); + + hookNewASTPreCompilation(unit.id(), unit.path(), unit.module()); + _pending_units.emplace_back(std::move(unit)); +} + +Result Driver::_symbol(const std::string& symbol) { + // Since `NULL` could be the address of a function, use `::dlerror` to + // detect errors. Since `::dlerror` resets the error state when called we + // can drive its state explicitly. + + ::dlerror(); // Resets error state. + auto sym = ::dlsym(RTLD_DEFAULT, symbol.c_str()); + + // We return an error if the symbol could not be looked up, or if the + // address of the symbol is `NULL`. + if ( auto error = ::dlerror() ) + return result::Error(error); + else if ( ! sym ) + return result::Error(util::fmt("address of symbol is %s", sym)); + + return sym; +} + +Result Driver::addInput(const std::filesystem::path& path) { + if ( path.empty() || _processed_paths.find(path) != _processed_paths.end() ) + return Nothing(); + + // Calling hook before stage check so that it can execute initialize() + // just in time if it so desires. + hookAddInput(path); + + if ( _stage == Stage::UNINITIALIZED ) + logger().internalError(" driver must be initialized before inputs can be added"); + + if ( _stage != Stage::INITIALIZED ) + logger().internalError("no further inputs can be added after compilation has finished already"); + + if ( plugin::registry().supportsExtension(path.extension()) ) { + HILTI_DEBUG(logging::debug::Driver, fmt("adding source file %s", path)); + + if ( auto unit = Unit::fromCache(_ctx, path) ) { + HILTI_DEBUG(logging::debug::Driver, fmt("reusing previously cached module %s", unit->id())); + _addUnit(std::move(*unit)); + } + else { + HILTI_DEBUG(logging::debug::Driver, fmt("parsing input file %s", path)); + unit = Unit::fromSource(context(), path); + if ( ! unit ) + return augmentError(unit.error()); + + _addUnit(std::move(*unit)); + } + + return Nothing(); + } + + else if ( path.extension() == ".cc" || path.extension() == ".cxx" ) { + HILTI_DEBUG(logging::debug::Driver, fmt("adding external C++ file %s", path)); + _external_cxxs.push_back(path); + return Nothing(); + } + + else if ( path.extension() == ".hlto" ) { + HILTI_DEBUG(logging::debug::Driver, fmt("adding precompiled HILTI file %s", path)); + + if ( auto load = Library(path).open(); ! load ) + return error(util::fmt("could not load library file %s: %s", path, load.error())); + + return Nothing(); + } + + return error("unsupported file type", path); +} + +Result Driver::addInput(hilti::Module&& module, const std::filesystem::path& path) { + if ( _processed_units.find(module.id()) != _processed_units.end() ) + return Nothing(); + + if ( (! path.empty()) && _processed_paths.find(path) != _processed_paths.end() ) + return Nothing(); + + // Calling hook before stage check so that it can execute initialize() + // just in time if it so desires. + hookAddInput(module, path); + + if ( _stage == Stage::UNINITIALIZED ) + logger().internalError(" driver must be initialized before inputs can be added"); + + if ( _stage != Stage::INITIALIZED ) + logger().internalError("no further inputs can be added after compilation has finished already"); + + HILTI_DEBUG(logging::debug::Driver, fmt("adding source AST %s", module.id())); + auto unit = Unit::fromModule(_ctx, std::move(module), path); + if ( ! unit ) + return augmentError(unit.error()); + + _addUnit(std::move(*unit)); + return Nothing(); +} + +Result Driver::_compileUnit(Unit unit) { + logging::DebugPushIndent _(logging::debug::Compiler); + + HILTI_DEBUG(logging::debug::Driver, fmt("processing input unit %s", unit.id())); + + if ( auto x = unit.compile(); ! x ) + // Specific errors have already been reported. + return error("aborting after errors"); + + hookNewASTPostCompilation(unit.id(), unit.path(), unit.module()); + + if ( _driver_options.output_hilti && ! _driver_options.include_linker ) { + // No need to kick off code generation. + _hlts.push_back(std::move(unit)); + return Nothing(); + } + + if ( auto rc = unit.codegen(); ! rc ) + return augmentError(rc.error()); + + if ( auto md = unit.linkerMetaData() ) + _mds.push_back(*md); + + if ( _driver_options.dump_code ) + dumpUnit(unit); + + if ( _driver_options.execute_code && ! _driver_options.skip_dependencies ) { + for ( const auto& d : unit.allImported(true) ) { + // Compile any implicit dependencies as well. Note that once we run + // the completion hook, that may compile further modules and hence in + // turn add more dependencies. + HILTI_DEBUG(logging::debug::Compiler, fmt("imported module %s needs compilation", d.id)); + + if ( auto rc = addInput(d.path); ! rc ) + return rc.error(); + } + } + + _hlts.push_back(std::move(unit)); + return Nothing(); +} + +Result Driver::compileUnits() { + if ( _stage != Stage::INITIALIZED ) + logger().internalError("unexpected driver stage in compileUnits()"); + + while ( _pending_units.size() ) { + auto pending_units = std::move(_pending_units); + _pending_units.clear(); + + for ( auto&& unit : pending_units ) { + if ( auto rc = _compileUnit(std::move(unit)); ! rc ) + return rc; + } + + if ( auto rc = hookCompilationFinished(); ! rc ) + return augmentError(rc.error()); + } + + _stage = Stage::FINALIZED; + + if ( _driver_options.output_hilti ) { + std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); + auto output = openOutput(output_path, false); + if ( ! output ) + return output.error(); + + for ( auto& unit : _hlts ) { + if ( ! unit.isCompiledHILTI() ) + continue; + + HILTI_DEBUG(logging::debug::Driver, util::fmt("saving HILTI code for module %s to %s", unit.id(), output)); + if ( ! unit.print(*output) ) + return error(fmt("error print HILTI code for module %s", unit.id())); + } + } + + return Nothing(); +} + +Result Driver::run() { + // Helper to print timing summary on exit from this function. + struct AtExit { //NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) + AtExit(std::function f) : func(std::move(f)) {} + ~AtExit() { func(); } + std::function func; + } _([&]() { + if ( _driver_options.report_times ) { + util::timing::summary(std::cerr); + util::type_erasure::summary(std::cerr); + } + }); + + initialize(); + + for ( const auto& i : _driver_options.inputs ) { + if ( auto rc = addInput(i); ! rc ) + return rc.error(); + } + + if ( auto x = compile(); ! x ) + return x; + + if ( ! _driver_options.execute_code || ! _driver_options.output_path.empty() ) + return Nothing(); + + try { + util::timing::Collector _("hilti/runtime"); + + if ( auto x = initRuntime(); ! x ) + return x; + + if ( auto x = executeMain(); ! x ) + return x; + + if ( auto x = finishRuntime(); ! x ) + return x; + + return Nothing(); + + } catch ( const std::exception& e ) { + std::cerr << util::fmt("[fatal error] terminating with uncaught exception of type %s: %s", + util::demangle(typeid(e).name()), e.what()) + << std::endl; + exit(1); + } + + return {}; +} + +Result Driver::compile() { + if ( auto rc = compileUnits(); ! rc ) + return rc; + + if ( _driver_options.include_linker ) { + if ( auto rc = linkUnits(); ! rc ) + return rc; + } + + if ( _driver_options.output_hilti ) + return Nothing(); + + if ( auto rc = outputUnits(); ! rc ) + return rc; + + if ( _driver_options.execute_code && ! _driver_options.output_prototypes ) { + if ( auto rc = jitUnits(); ! rc ) + return rc; + + if ( ! _driver_options.output_path.empty() ) { + // Save code to disk rather than execute. + HILTI_DEBUG(logging::debug::Driver, fmt("saving precompiled code to %s", _driver_options.output_path)); + + assert(_jit); + auto library = _jit->retrieveLibrary(); + if ( ! library ) + return library.error(); + + if ( auto success = library->get().save(_driver_options.output_path); ! success ) + return result::Error( + fmt("error saving object code to %s: %s", _driver_options.output_path, success.error())); + } + } + + return Nothing(); +} + +Result Driver::linkUnits() { + if ( _stage != Stage::FINALIZED ) + logger().internalError("unexpected driver stage in linkModule()"); + + _stage = Stage::LINKED; + + for ( const auto& cxx : _external_cxxs ) { + std::ifstream in; + + if ( auto x = openInput(in, cxx); ! x ) + return x.error(); + + auto md = Unit::readLinkerMetaData(in, cxx); + + if ( ! md.first ) + return error(fmt("cannot read linker data from %s", cxx)); + + if ( md.second ) + _mds.push_back(*md.second); + } + + HILTI_DEBUG(logging::debug::Driver, "linking modules"); + for ( const auto& md : _mds ) { + auto id = md.at("module").template get(); + HILTI_DEBUG(logging::debug::Driver, fmt(" - %s", id)); + } + + auto linker_unit = Unit::link(_ctx, _mds); + + if ( ! linker_unit ) + return error("aborting after linker errors"); + + if ( _driver_options.output_linker ) { + std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); + + auto output = openOutput(output_path, false); + if ( ! output ) + return output.error(); + + HILTI_DEBUG(logging::debug::Driver, fmt("writing linker code to %s", output_path)); + linker_unit->cxxCode()->save(*output); + return Nothing(); // All done. + } + + if ( _driver_options.dump_code ) + dumpUnit(*linker_unit); + + if ( linker_unit->cxxCode()->code() && linker_unit->cxxCode()->code()->size() ) + _hlts.push_back(std::move(*linker_unit)); + + return Nothing(); +} + +Result Driver::outputUnits() { + if ( _stage != Stage::FINALIZED && _stage != Stage::LINKED ) + logger().internalError("unexpected driver stage in outputUnits()"); + + std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); + + for ( auto& unit : _hlts ) { + if ( auto cxx = unit.cxxCode() ) { + if ( _driver_options.output_cxx ) { + auto output = openOutput(output_path, false); + if ( ! output ) + return output.error(); + + HILTI_DEBUG(logging::debug::Driver, fmt("saving C++ code for module %s to %s", unit.id(), output_path)); + cxx->save(*output); + } + + if ( _driver_options.output_prototypes ) { + auto output = openOutput(output_path, false); + if ( ! output ) + return output.error(); + + HILTI_DEBUG(logging::debug::Driver, + fmt("saving C++ prototypes for module %s to %s", unit.id(), output_path)); + unit.createPrototypes(*output); + } + + if ( _driver_options.output_dependencies != driver::Dependencies::None ) { + for ( const auto& [id, path] : + unit.allImported(_driver_options.output_dependencies == driver::Dependencies::Code) ) + std::cout << fmt("%s (%s)\n", id, util::normalizePath(path).native()); + } + + _generated_cxxs.push_back(std::move(*cxx)); + } + else + return error(fmt("error retrieving C++ code for module %s", unit.id())); + } + + return Nothing(); +} + +Result Driver::jitUnits() { + if ( _stage != Stage::LINKED ) + logger().internalError("unexpected driver stage in jitModule()"); + + _stage = Stage::JITTED; + + if ( _driver_options.disable_jit ) + return error("driver not configured for JIT"); + + static util::timing::Ledger ledger("hilti/jit"); + util::timing::Collector _(&ledger); + + HILTI_DEBUG(logging::debug::Driver, "JIT modules:"); + + auto jit = std::make_unique(_ctx); + + for ( const auto& cxx : _generated_cxxs ) { + HILTI_DEBUG(logging::debug::Driver, fmt(" - %s", cxx.id())); + jit->add(cxx); + } + + for ( const auto& cxx : _external_cxxs ) { + HILTI_DEBUG(logging::debug::Driver, fmt(" - %s", cxx)); + jit->add(cxx); + } + + if ( jit->needsCompile() ) { + HILTI_DEBUG(logging::debug::Driver, "JIT: compiling to bitcode"); + if ( ! jit->compile() ) { + return error("JIT compilation failed"); + } + } + + HILTI_DEBUG(logging::debug::Driver, "JIT: preparing native code"); + if ( _driver_options.dump_code ) + jit->setDumpCode(); + + if ( auto rc = jit->jit(); ! rc ) + return rc; + + _jit = std::move(jit); + return Nothing(); +} + +void Driver::printHiltiException(const hilti::rt::Exception& e) { + std::cerr << fmt("uncaught exception %s: %s", util::demangle(typeid(e).name()), e.what()) << std::endl; + + if ( _driver_options.show_backtraces && ! e.backtrace().empty() ) { + std::cerr << "backtrace:\n"; + + for ( const auto& s : e.backtrace() ) + std::cerr << " " << s << "\n"; + } +} + +Result Driver::initRuntime() { + util::timing::Collector _("hilti/runtime/init"); + + if ( _runtime_initialized ) + return Nothing(); + + if ( _requires_jit() && ! _jit ) + return result::Error("JIT not initialized"); + + auto config = hilti::rt::configuration::get(); + config.abort_on_exceptions = _driver_options.abort_on_exceptions; + config.show_backtraces = _driver_options.show_backtraces; + hilti::rt::configuration::set(config); + + try { + HILTI_DEBUG(logging::debug::Driver, "initializing runtime"); + + if ( _requires_jit() ) + _jit->initRuntime(); + + rt::init(); + hookInitRuntime(); + } catch ( const hilti::rt::Exception& e ) { + printHiltiException(e); + exit(1); + } catch ( const std::runtime_error& e ) { + std::cerr << fmt("uncaught C++ exception %s: %s", util::demangle(typeid(e).name()), e.what()) << std::endl; + exit(1); + } + + _runtime_initialized = true; + return Nothing(); +} + +Result Driver::executeMain() { + util::timing::Collector _("hilti/runtime/main"); + + int rc = 0; + + if ( auto main = _symbol("hilti_main") ) { + HILTI_DEBUG(logging::debug::Driver, "executing main() function"); + + using main_t = int(); + + try { + rc = (*(reinterpret_cast(*main)))(); + } catch ( const hilti::rt::Exception& e ) { + printHiltiException(e); + exit(1); + } catch ( const std::runtime_error& e ) { + std::cerr << fmt("uncaught C++ exception %s: %s", util::demangle(typeid(e).name()), e.what()) << std::endl; + exit(1); + } + } + + if ( rc == 0 ) + return Nothing(); + + return error(fmt("hilti_main() returned exit code %d", rc)); +} + +Result Driver::finishRuntime() { + util::timing::Collector _("hilti/runtime/finish"); + + if ( _runtime_initialized ) { + HILTI_DEBUG(logging::debug::Driver, "shutting down runtime"); + hookFinishRuntime(); + rt::done(); + _runtime_initialized = false; + } + + if ( _jit ) { + _jit->finishRuntime(); + _jit.reset(); + } + + return Nothing(); +} + +Result Driver::jit() { + if ( ! _jit ) + return error("cannot retrieve JIT instance, JIT compilation hasn't been performed"); + + if ( ! _runtime_initialized ) + return error("cannot retrieve JIT instance, runtime hasn't been initialized"); + + return _jit.get(); +} diff --git a/hilti/src/compiler/jit.cc b/hilti/src/compiler/jit.cc new file mode 100644 index 000000000..7577eca06 --- /dev/null +++ b/hilti/src/compiler/jit.cc @@ -0,0 +1,231 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +#include +#include +#include +#include + +using namespace hilti; + +CxxCode::CxxCode(const detail::cxx::Unit& u) { + std::stringstream buffer; + u.print(buffer); + load(u.moduleID(), buffer); +} + +bool CxxCode::load(const std::filesystem::path& path) { + std::ifstream in; + in.open(path); + + if ( ! in ) + return false; + + if ( ! load(path, in) ) + return false; + + _id = path; + return true; +} + +bool CxxCode::load(const std::string& id, std::istream& in) { + std::string code{std::istreambuf_iterator(in), std::istreambuf_iterator()}; + + if ( in.fail() ) + return false; + + _id = id; + _code = std::move(code); + return true; +} + +bool CxxCode::save(const std::filesystem::path& p) const { + if ( ! _code ) + return false; + + std::ofstream out(p); + + if ( ! out ) + return false; + + out << *_code; + out.close(); + return ! out.fail(); +} + +bool CxxCode::save(std::ostream& out) const { + if ( ! _code ) + return false; + + out << *_code; + return ! out.fail(); +} + +Library::Library(const std::filesystem::path& path) { + auto path_ = util::createTemporaryFile(path.filename()); + if ( ! path_ ) { + logger().fatalError(util::fmt("could not add library %s: %s", path, path_.error())); + } + + std::error_code ec; + std::filesystem::copy(path, *path_, std::filesystem::copy_options::overwrite_existing, ec); + if ( ec ) + logger().fatalError(util::fmt("could not store library %s at %s: %s", path, *path_, ec.message())); + + _path = std::filesystem::absolute(std::move(*path_)); +} + +Library::~Library() { + std::error_code ec; + std::filesystem::remove(_path, ec); + + if ( ec ) { + logger().error(util::fmt("could not remove library %s from store: %s", ec.message())); + } +} + +Result Library::open() const { + constexpr auto mode = RTLD_LAZY | RTLD_GLOBAL; + + if ( ! ::dlopen(_path.c_str(), mode) ) + return result::Error(util::fmt("failed to load library %s: %s", _path, dlerror())); + + return Nothing(); +} + +Result Library::save(const std::filesystem::path& path) const { + std::error_code ec; + std::filesystem::copy(_path, path, std::filesystem::copy_options::overwrite_existing, ec); + + if ( ec ) { + return result::Error(ec.message()); + } + + return Nothing(); +} + +#ifdef HILTI_HAVE_JIT +#include + +JIT::JIT(std::shared_ptr context) : _context(std::move(context)) { + _jit = std::make_unique(_context); +} + +JIT::~JIT() { + if ( _jit ) + finishRuntime(); +} + +bool JIT::compile() { + util::timing::Collector _("hilti/jit/compile"); + + if ( ! _jit ) + return false; + + if ( _codes.empty() && _files.empty() ) + return false; + + for ( const auto& c : _codes ) { + HILTI_DEBUG(logging::debug::Jit, util::fmt("jitting %s", c.id())); + + if ( ! _jit->compile(c) ) { + logger().error(util::fmt("jit: failed to compile C++ code unit %s to bitcode", c.id())); + return false; + } + } + + for ( const auto& c : _files ) { + HILTI_DEBUG(logging::debug::Jit, util::fmt("jitting %s", c)); + + if ( ! _jit->compile(c) ) { + logger().error(util::fmt("jit: failed to compile C++ file %s to bitcode", c)); + return false; + } + } + + return true; +} + +void JIT::setDumpCode() { + if ( _jit ) + _jit->setDumpCode(); +} + +hilti::Result JIT::jit() { + util::timing::Collector _("hilti/jit/jit"); + + if ( ! _jit ) + return result::Error("jit not initialized"); + + return _jit->jit(); +} + +Result> JIT::retrieveLibrary() const { + if ( ! _jit ) { + return result::Error("no JIT object code available"); + } + + return *_jit->retrieveLibrary(); +} + +bool JIT::initRuntime() { + if ( ! _jit ) + return false; + + return _jit->initRuntime(); +} + +bool JIT::finishRuntime() { + if ( ! _jit ) + return false; + + _jit->finishRuntime(); + _jit.reset(); + return true; +} + +std::string JIT::compilerVersion() { return detail::ClangJIT::compilerVersion(); } + +#else + +class detail::ClangJIT {}; + +JIT::JIT(std::shared_ptr context) : _context(std::move(context)) {} + +JIT::~JIT() {} + +bool JIT::compile() { + logger().error("jit: support for just-in-time compilation not available"); + return false; +} + +Result JIT::jit() { + logger().error("jit: support for just-in-time compilation not available"); + return Nothing(); +} + +void JIT::setDumpCode() { logger().error("jit: support for just-in-time compilation not available"); } + +Result> JIT::retrieveLibrary() const { + constexpr char message[] = "jit: support for just-in-time compilation not available"; + logger().error(message); + return result::Error(message); +} + +bool JIT::initRuntime() { + logger().error("jit: support for just-in-time compilation not available"); + return false; +} + +bool JIT::finishRuntime() { + logger().error("jit: support for just-in-time compilation not available"); + return false; +} + +std::string JIT::compilerVersion() { return ""; } + +#endif diff --git a/hilti/src/compiler/parser/driver.cc b/hilti/src/compiler/parser/driver.cc new file mode 100644 index 000000000..0f70415ed --- /dev/null +++ b/hilti/src/compiler/parser/driver.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include + +/** We compile with a source property to find this. */ +#include <__parser.h> + +using namespace hilti; +using namespace hilti::detail::parser; + +Result hilti::parseSource(std::istream& in, const std::string& filename) { + return Driver().parse(in, filename); +} + +Result Driver::parse(std::istream& in, const std::string& filename) { + auto old_errors = logger().errors(); + _filename = filename; + + Scanner scanner(&in); + _scanner = &scanner; + + Parser parser(this); + _parser = &parser; + + hilti::logging::Stream dbg_stream_parser(hilti::logging::debug::Parser); + + if ( logger().isEnabled(logging::debug::Parser) ) { + _parser->set_debug_stream(dbg_stream_parser); + _parser->set_debug_level(1); + } + + _expression_mode = 1; + _scanner->enableExpressionMode(); + _parser->parse(); + + if ( logger().errors() > old_errors ) + return result::Error("parse error"); + + return {std::move(_module)}; +} + +void Driver::error(const std::string& msg, const Meta& m) { logger().error(msg, m.location()); } + +void Driver::disablePatternMode() { _scanner->disablePatternMode(); } + +void Driver::enablePatternMode() { _scanner->enablePatternMode(); } + +void Driver::disableExpressionMode() { + if ( --_expression_mode == 0 ) + _scanner->disableExpressionMode(); +} + +void Driver::enableExpressionMode() { + if ( _expression_mode++ == 0 ) + _scanner->enableExpressionMode(); +} + +void Driver::disableDottedIDMode() { _scanner->disableDottedIDMode(); } + +void Driver::enableDottedIDMode() { _scanner->enableDottedIDMode(); } diff --git a/hilti/src/compiler/parser/parser.yy b/hilti/src/compiler/parser/parser.yy new file mode 100644 index 000000000..24a8e3470 --- /dev/null +++ b/hilti/src/compiler/parser/parser.yy @@ -0,0 +1,847 @@ +/* Copyright (c) 2020 by the Zeek Project. See LICENSE for details. */ + +%skeleton "lalr1.cc" /* -*- C++ -*- */ +%require "3.4" +%defines + +%{ +namespace hilti { namespace detail { class Parser; } } + +#include + +%} + +%locations +%initial-action +{ + @$.begin.filename = @$.end.filename = driver->currentFile(); +}; + +%parse-param {class Driver* driver} +%lex-param {class Driver* driver} + +%define api.namespace {hilti::detail::parser} +%define api.parser.class {Parser} +%define parse.error verbose + +%debug +%verbose + +%glr-parser +%expect 94 +%expect-rr 176 + +%union {} +%{ + +#include + +#undef yylex +#define yylex driver->scanner()->lex + +static hilti::Meta toMeta(hilti::detail::parser::location l) { + return hilti::Meta(hilti::Location(*l.begin.filename, l.begin.line, l.end.line)); +} + +static hilti::Type iteratorForType(hilti::Type t, bool const_, hilti::Meta m) { + if ( hilti::type::isIterable(t) ) + return t.iteratorType(const_); + else { + hilti::logger().error(util::fmt("type '%s' is not iterable", t), m.location()); + return hilti::type::Error(m); + } +} + +static hilti::Type viewForType(hilti::Type t, hilti::Meta m) { + if ( hilti::type::isViewable(t) ) + return t.viewType(); + else { + hilti::logger().error(util::fmt("type '%s' is not viewable", t), m.location()); + return hilti::type::Error(m); + } +} + +#define __loc__ toMeta(yylhs.location) + +%} + +%token IDENT "identifier" +%token SCOPED_IDENT "scoped identifier" +%token DOTTED_IDENT "dotted identifier" +%token ATTRIBUTE "attribute" +%token PROPERTY "property" + +%token CSTRING "string value" +%token CBYTES "bytes value" +%token CREGEXP "regular expression value" +%token CADDRESS "address value" +%token CPORT "port value" +%token CUREAL "real value" +%token CUINTEGER "unsigned integer value" +%token CBOOL "bool value" + +%token EOD 0 "" + +%token ADD "add" +%token ASSERT "assert" +%token ASSERT_EXCEPTION "assert-exception" +%token ADDRESS "addr" +%token AFTER "after" +%token AND "&&" +%token ANY "any" +%token ARROW "->" +%token AUTO "auto" +%token AT "at" +%token BEGIN_ "begin" +%token BOOL "bool" +%token BREAK "break" +%token BYTES "bytes" +%token CADDR "caddr" +%token CALLABLE "callable" +%token CASE "case" +%token CAST "cast" +%token CATCH "catch" +%token CHANNEL "channel" +%token CLASSIFIER "classifier" +%token CNULL "Null" +%token CONST "const" +%token CONTINUE "continue" +%token CONTEXT "context" +%token COPY "copy" +%token DECLARE "declare" +%token DEFAULT "default" +%token DELETE "delete" +%token DIVIDEASSIGN "/=" +%token DOLLARDOLLAR "$$" +%token DOTDOT ".." +%token REAL "real" +%token ELSE "else" +%token END_ "end" +%token ENUM "enum" +%token EQ "==" +%token ERROR "error" +%token EXCEPTION "exception" +%token EXPORT "export" +%token EXTERN "extern" +%token FILE "file" +%token FOR "for" +%token FROM "from" +%token FUNCTION "function" +%token GEQ ">=" +%token GLOBAL "global" +%token HASATTR "?." +%token HOOK "hook" +%token IF "if" +%token IMPORT "import" +%token IN "in" +%token INIT "init" +%token INOUT "inout" +%token INT "int" +%token INTERVAL "interval" +%token IOSRC "iosrc" +%token ITERATOR "iterator" +%token CONST_ITERATOR "const_iterator" +%token LEQ "<=" +%token LIBRARY_TYPE "library type" +%token LIST "list" +%token LOCAL "local" +%token MAP "map" +%token MATCH_TOKEN_STATE "match_token_state" +%token METHOD "method" +%token MINUSASSIGN "-=" +%token MINUSMINUS "--" +%token MOD "%" +%token MODULE "module" +%token MOVE "move" +%token NEW "new" +%token NEQ "!=" +%token NETWORK "net" +%token OPTIONAL "optional" +%token OR "||" +%token OVERLAY "overlay" +%token PLUSASSIGN "+=" +%token PLUSPLUS "++" +%token PORT "port" +%token POW "**" +%token PRIVATE "private" +%token PUBLIC "public" +%token STRONG_REF "strong_ref" +%token REGEXP "regexp" +%token RESULT "result" +%token RETURN "return" +%token SCOPE "scope" +%token SELF "self" +%token SET "set" +%token SHIFTLEFT "<<" +%token SHIFTRIGHT ">>" +%token STREAM "stream" +%token STRING "string" +%token STRUCT "struct" +%token SWITCH "switch" +%token TIME "time" +%token TIMER "timer" +%token TIMERMGR "timer_mgr" +%token TIMESASSIGN "*=" +%token THROW "throw" +%token TRY "try" +%token TRYATTR ".?" +%token TUPLE "tuple" +%token TYPE "type" +%token UINT "uint" +%token UNION "union" +%token UNPACK "unpack" +%token VECTOR "vector" +%token VIEW "view" +%token VOID "void" +%token WHILE "while" +%token WITH "with" +%token VALUE_REF "value_ref" +%token WEAK_REF "weak_ref" +%token YIELD "yield" + +%type local_id scoped_id dotted_id +%type local_decl local_init_decl global_decl type_decl import_decl constant_decl function_decl global_scope_decl property_decl +%type global_scope_items +%type base_type_no_attrs base_type type function_type tuple_type struct_type enum_type union_type +%type ctor tuple struct_ list regexp map set +%type expr tuple_elem tuple_expr member_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g +%type opt_tuple_elems1 opt_tuple_elems2 exprs opt_exprs opt_type_arguments +%type opt_func_default_expr +%type function_with_body function_without_body +%type func_param +%type opt_func_param_kind +%type func_result +%type func_flavor opt_func_flavor +%type opt_func_cc +%type opt_linkage +%type func_params opt_func_params opt_struct_params +%type stmt stmt_decl stmt_expr block braced_block +%type stmts opt_stmts +%type opt_else_block +%type attribute +%type opt_attributes +%type tuple_type_elem +%type tuple_type_elems +%type struct_field +%type struct_fields +%type struct_elems +%type struct_elem +%type union_field +%type union_fields opt_union_fields +%type map_elems opt_map_elems +%type map_elem +%type enum_label +%type enum_labels +%type re_patterns +%type re_pattern_constant +%type switch_case +%type switch_cases +%type try_catch +%type try_catches +%type opt_type_flags /* type_flag */ +%type const_real +%type const_sint +%type const_uint + +%% + +%start module; + +module : MODULE local_id '{' + global_scope_items '}' { auto m = hilti::Module($2, std::move($4.first), std::move($4.second), __loc__); + driver->setDestinationModule(std::move(m)); + } + ; + +/* IDs */ + +local_id : IDENT { std::string name($1); + + if (name.find('-') != std::string::npos) + hilti::logger().error(util::fmt("Invalid ID '%s': cannot contain '-'", name), __loc__.location()); + if (name.substr(0, 2) == "__") + hilti::logger().error(util::fmt("Invalid ID '%s': cannot start with '__'", name), __loc__.location()); + + $$ = hilti::ID(std::move(name), __loc__); + } + +scoped_id : local_id { $$ = std::move($1); } + | SCOPED_IDENT { $$ = hilti::ID($1, __loc__); } + +dotted_id : { driver->enableDottedIDMode(); } + DOTTED_IDENT + { driver->disableDottedIDMode(); } { $$ = hilti::ID($2, __loc__); } + +/* Declarations */ + +global_scope_items + : global_scope_items global_scope_decl + { $$ = std::move($1); $$.first.push_back($2); } + | global_scope_items stmt + { $$ = std::move($1); $$.second.push_back($2); } + | /* empty */ { } + ; + +global_scope_decl + : type_decl { $$ = std::move($1); } + | constant_decl { $$ = std::move($1); } + | global_decl { $$ = std::move($1); } + | function_decl { $$ = std::move($1); } + | import_decl { $$ = std::move($1); } + | property_decl { $$ = std::move($1); } + +type_decl : opt_linkage TYPE scoped_id '=' type opt_attributes ';' + { $$ = hilti::declaration::Type(std::move($3), std::move($5), std::move($6), std::move($1), __loc__); } + +constant_decl : opt_linkage CONST scoped_id '=' expr ';' + { $$ = hilti::declaration::Constant($3, $5, $1, __loc__); } + +local_decl : LOCAL local_id '=' expr ';' { $$ = hilti::declaration::LocalVariable($2, $4.type(), $4, false, __loc__); } + | LOCAL type local_id opt_type_arguments';' { $$ = hilti::declaration::LocalVariable($3, $2, $4, {}, false, __loc__); } + | LOCAL type local_id '=' expr ';' + { $$ = hilti::declaration::LocalVariable($3, $2, $5, false, __loc__); } + | LOCAL AUTO local_id '=' expr ';' + { $$ = hilti::declaration::LocalVariable($3, $5, false, __loc__); } + ; + +local_init_decl + : LOCAL type local_id '=' expr + { $$ = hilti::declaration::LocalVariable($3, $2, $5, false, __loc__); } + | LOCAL AUTO local_id '=' expr + { $$ = hilti::declaration::LocalVariable($3, $5, false, __loc__); } + ; + +global_decl : opt_linkage GLOBAL scoped_id '=' expr ';' + { $$ = hilti::declaration::GlobalVariable($3, $5.type(), $5, $1, __loc__); } + | opt_linkage GLOBAL type scoped_id opt_type_arguments ';' + { $$ = hilti::declaration::GlobalVariable($4, $3, $5, {}, $1, __loc__); } + | opt_linkage GLOBAL type scoped_id '=' expr ';' + { $$ = hilti::declaration::GlobalVariable($4, $3, $6, $1, __loc__); } + | opt_linkage GLOBAL AUTO scoped_id '=' expr ';' + { $$ = hilti::declaration::GlobalVariable($4, $6, $1, __loc__); } + ; + +opt_type_arguments + : '(' opt_exprs ')' { $$ = std::move($2); } + | /* empty */ { $$ = std::vector{}; } + +function_decl : opt_linkage FUNCTION function_with_body + { $$ = hilti::declaration::Function($3, $1, __loc__); } + | METHOD function_with_body { $$ = hilti::declaration::Function($2, hilti::declaration::Linkage::Struct, __loc__); } + | DECLARE opt_linkage function_without_body ';' + { $$ = hilti::declaration::Function($3, $2, __loc__); } + ; + +import_decl : IMPORT local_id ';' { $$ = hilti::declaration::ImportedModule(std::move($2), std::string(".hlt"), __loc__); } + | IMPORT local_id FROM dotted_id ';' { $$ = hilti::declaration::ImportedModule(std::move($2), std::string(".hlt"), std::move($4), __loc__); } + ; + + +property_decl : PROPERTY ';' { $$ = hilti::declaration::Property(ID(std::move($1)), __loc__); } + | PROPERTY '=' expr ';' { $$ = hilti::declaration::Property(ID(std::move($1)), std::move($3), __loc__); } + +opt_linkage : PUBLIC { $$ = hilti::declaration::Linkage::Public; } + | PRIVATE { $$ = hilti::declaration::Linkage::Private; } + | INIT { $$ = hilti::declaration::Linkage::Init; } + | /* empty */ { $$ = hilti::declaration::Linkage::Private; } + +/* Functions */ + +function_with_body + : opt_func_flavor opt_func_cc func_result scoped_id '(' opt_func_params ')' opt_attributes braced_block + { + auto ftype = hilti::type::Function($3, $6, $1, __loc__); + $$ = hilti::Function($4, std::move(ftype), $9, $2, $8, __loc__); + } + +function_without_body + : opt_func_flavor opt_func_cc func_result scoped_id '(' opt_func_params ')' opt_attributes + { + auto ftype = hilti::type::Function($3, $6, $1, __loc__); + $$ = hilti::Function($4, std::move(ftype), {}, $2, $8, __loc__); + } + +opt_func_flavor : func_flavor { $$ = $1; } + | /* empty */ { $$ = hilti::type::function::Flavor::Standard; } + +func_flavor : METHOD { $$ = hilti::type::function::Flavor::Method; } + | HOOK { $$ = hilti::type::function::Flavor::Hook; } + +opt_func_cc : EXTERN { $$ = hilti::function::CallingConvention::Extern; } + | /* empty */ { $$ = hilti::function::CallingConvention::Standard; } + + +opt_func_params : func_params { $$ = std::move($1); } + | /* empty */ { $$ = std::vector{}; } + +func_params : func_params ',' func_param { $$ = std::move($1); $$.push_back($3); } + | func_param { $$ = std::vector{$1}; } + +func_param : opt_func_param_kind type local_id opt_func_default_expr + { $$ = hilti::type::function::Parameter($3, $2, $1, $4, __loc__); } + +func_result : type { $$ = hilti::type::function::Result(std::move($1), __loc__); } + +opt_func_param_kind + : COPY { $$ = hilti::declaration::parameter::Kind::Copy; } + | INOUT { $$ = hilti::declaration::parameter::Kind::InOut; } + | /* empty */ { $$ = hilti::declaration::parameter::Kind::In; } + ; + +opt_func_default_expr : '=' expr { $$ = std::move($2); } + | /* empty */ { $$ = {}; } + ; + +/* Statements */ + +block : braced_block { $$ = std::move($1); } + | stmt { $$ = hilti::statement::Block({$1}, __loc__); } + ; + +braced_block : '{' opt_stmts '}' { $$ = hilti::statement::Block(std::move($2), __loc__); } + +opt_stmts : stmts { $$ = std::move($1); } + | /* empty */ { $$ = std::vector{}; } + +stmts : stmts stmt { $$ = std::move($1); $$.push_back($2); } + | stmt { $$ = std::vector{std::move($1)}; } + +stmt : stmt_expr ';' { $$ = std::move($1); } + | stmt_decl { $$ = std::move($1); } + | RETURN ';' { $$ = hilti::statement::Return(__loc__); } + | RETURN expr ';' { $$ = hilti::statement::Return(std::move($2), __loc__); } + | THROW expr ';' { $$ = hilti::statement::Throw(std::move($2), __loc__); } + | THROW ';' { $$ = hilti::statement::Throw(__loc__); } + | YIELD ';' { $$ = hilti::statement::Yield(__loc__); } + | BREAK ';' { $$ = hilti::statement::Break(__loc__); } + | CONTINUE ';' { $$ = hilti::statement::Continue(__loc__); } + | IF '(' expr ')' block opt_else_block + { $$ = hilti::statement::If(std::move($3), std::move($5), std::move($6), __loc__); } + | IF '(' local_init_decl ')' block opt_else_block + { $$ = hilti::statement::If(std::move($3), {}, std::move($5), std::move($6), __loc__); } + | IF '(' local_init_decl ';' expr ')' block opt_else_block + { $$ = hilti::statement::If(std::move($3), std::move($5), std::move($7), std::move($8), __loc__); } + | WHILE '(' local_init_decl ';' expr ')' block opt_else_block + { $$ = hilti::statement::While(std::move($3), std::move($5), std::move($7), std::move($8), __loc__); } + | WHILE '(' expr ')' block opt_else_block + { $$ = hilti::statement::While(std::move($3), std::move($5), std::move($6), __loc__); } + | WHILE '(' local_init_decl ')' block opt_else_block + { $$ = hilti::statement::While(std::move($3), {}, std::move($5), std::move($6), __loc__); } + | FOR '(' local_id IN expr ')' block + { $$ = hilti::statement::For(std::move($3), std::move($5), std::move($7), __loc__); } + | SWITCH '(' expr ')' '{' switch_cases '}' + { $$ = hilti::statement::Switch(std::move($3), std::move($6), __loc__); } + | SWITCH '(' local_init_decl ')' '{' switch_cases '}' + { $$ = hilti::statement::Switch($3, expression::UnresolvedID($3.as().id()), std::move($6), __loc__); } + | TRY block try_catches + { $$ = hilti::statement::Try(std::move($2), std::move($3), __loc__); } + | ASSERT expr ';' { $$ = hilti::statement::Assert(std::move($2), {}, __loc__); } + | ASSERT expr ':' expr ';' { $$ = hilti::statement::Assert(std::move($2), std::move($4), __loc__); } + | ASSERT_EXCEPTION expr ';' { $$ = hilti::statement::Assert(hilti::statement::assert::Exception(), std::move($2), {}, {}, __loc__); } + | ASSERT_EXCEPTION expr ':' expr ';' + { $$ = hilti::statement::Assert(hilti::statement::assert::Exception(), std::move($2), {}, std::move($4), __loc__); } + + | ADD expr ';' { auto op = $2.tryAs(); + if ( ! (op && op->kind() == hilti::operator_::Kind::Index) ) + error(@$, "'add' must be used with index expression only"); + + auto expr = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Add, op->operands(), __loc__); + $$ = hilti::statement::Expression(std::move(expr), __loc__); + } + + + | DELETE expr ';' { auto op = $2.tryAs(); + if ( ! (op && op->kind() == hilti::operator_::Kind::Index) ) + error(@$, "'add' must be used with index expression only"); + + auto expr = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Delete, op->operands(), __loc__); + $$ = hilti::statement::Expression(std::move(expr), __loc__); + } + ; + +opt_else_block + : ELSE block { $$ = std::move($2); } + | /* empty */ { $$ = {}; } + +switch_cases : switch_cases switch_case { $$ = std::move($1); $$.push_back(std::move($2)); } + | switch_case { $$ = std::vector({ std::move($1) }); } + +switch_case : CASE exprs ':' block { $$ = hilti::statement::switch_::Case(std::move($2), std::move($4), __loc__); } + | DEFAULT ':' block { $$ = hilti::statement::switch_::Case(hilti::statement::switch_::Default(), std::move($3), __loc__); } + +try_catches : try_catches try_catch { $$ = std::move($1); $$.push_back(std::move($2)); } + | try_catch { $$ = std::vector({ std::move($1) }); } + +try_catch : CATCH '(' type local_id ')' block + { $$ = hilti::statement::try_::Catch(declaration::Parameter($4, $3, declaration::parameter::Kind::In, {}, __loc__), std::move($6), __loc__); } + | CATCH block { $$ = hilti::statement::try_::Catch(std::move($2), __loc__); } + +stmt_decl : local_decl { $$ = hilti::statement::Declaration($1, __loc__); } + | type_decl { $$ = hilti::statement::Declaration($1, __loc__); } + | constant_decl { $$ = hilti::statement::Declaration($1, __loc__); } + ; + +stmt_expr : expr { $$ = hilti::statement::Expression($1, __loc__); } + +/* Types */ + +base_type_no_attrs + : ANY { $$ = hilti::type::Any(__loc__); } + | ADDRESS { $$ = hilti::type::Address(__loc__); } + | BOOL { $$ = hilti::type::Bool(__loc__); } + | BYTES { $$ = hilti::type::Bytes(__loc__); } + | ERROR { $$ = hilti::type::Error(__loc__); } + | INTERVAL { $$ = hilti::type::Interval(__loc__); } + | NETWORK { $$ = hilti::type::Network(__loc__); } + | PORT { $$ = hilti::type::Port(__loc__); } + | REAL { $$ = hilti::type::Real(__loc__); } + | REGEXP { $$ = hilti::type::RegExp(__loc__); } + | STREAM { $$ = hilti::type::Stream(__loc__); } + | STRING { $$ = hilti::type::String(__loc__); } + | TIME { $$ = hilti::type::Time(__loc__); } + | VOID { $$ = hilti::type::Void(__loc__); } + + | INT type_param_begin CUINTEGER type_param_end { $$ = hilti::type::SignedInteger($3, __loc__); } + | INT type_param_begin '*' type_param_end { $$ = hilti::type::SignedInteger(hilti::type::Wildcard(), __loc__); } + | UINT type_param_begin CUINTEGER type_param_end { $$ = hilti::type::UnsignedInteger($3, __loc__); } + | UINT type_param_begin '*' type_param_end { $$ = hilti::type::UnsignedInteger(hilti::type::Wildcard(), __loc__); } + + | OPTIONAL type_param_begin type type_param_end { $$ = hilti::type::Optional($3, __loc__); } + | RESULT type_param_begin type type_param_end { $$ = hilti::type::Result($3, __loc__); } + | VIEW type_param_begin type type_param_end { $$ = viewForType(std::move($3), __loc__); } + | ITERATOR type_param_begin type type_param_end { $$ = iteratorForType(std::move($3), false, __loc__); } + | CONST_ITERATOR type_param_begin type type_param_end { $$ = iteratorForType(std::move($3), true, __loc__); } + | STRONG_REF type_param_begin type type_param_end { $$ = hilti::type::StrongReference($3, __loc__); } + | STRONG_REF type_param_begin '*' type_param_end { $$ = hilti::type::StrongReference(hilti::type::Wildcard(), __loc__); } + | VALUE_REF type_param_begin type type_param_end { $$ = hilti::type::ValueReference($3, __loc__); } + | VALUE_REF type_param_begin '*' type_param_end { $$ = hilti::type::ValueReference(hilti::type::Wildcard(), __loc__); } + | WEAK_REF type_param_begin type type_param_end { $$ = hilti::type::WeakReference($3, __loc__); } + | WEAK_REF type_param_begin '*' type_param_end { $$ = hilti::type::WeakReference(hilti::type::Wildcard(), __loc__); } + + | LIST type_param_begin '*' type_param_end { $$ = hilti::type::List(hilti::type::Wildcard(), __loc__); } + | LIST type_param_begin type type_param_end { $$ = hilti::type::List(std::move($3), __loc__); } + | VECTOR type_param_begin '*' type_param_end { $$ = hilti::type::Vector(hilti::type::Wildcard(), __loc__); } + | VECTOR type_param_begin type type_param_end { $$ = hilti::type::Vector(std::move($3), __loc__); } + | SET type_param_begin '*' type_param_end { $$ = hilti::type::Set(hilti::type::Wildcard(), __loc__); } + | SET type_param_begin type type_param_end { $$ = hilti::type::Set(std::move($3), __loc__); } + | MAP type_param_begin '*' type_param_end { $$ = hilti::type::Map(hilti::type::Wildcard(), __loc__); } + | MAP type_param_begin type ',' type type_param_end { $$ = hilti::type::Map(std::move($3), std::move($5), __loc__); } + + | EXCEPTION { $$ = hilti::type::Exception(__loc__); } + | EXCEPTION ':' type { $$ = hilti::type::Exception(std::move($3), __loc__); } + + | LIBRARY_TYPE '(' CSTRING ')' { $$ = hilti::type::Library(std::move($3), __loc__); } + + | tuple_type { $$ = std::move($1); } + | struct_type { $$ = std::move($1); } + | union_type { $$ = std::move($1); } + | enum_type { $$ = std::move($1); } + ; + +base_type : base_type_no_attrs opt_type_flags + { $$ = type::addFlags($1, $2); } + ; + +type : base_type { $$ = std::move($1); } + | function_type { $$ = std::move($1); } + | scoped_id { $$ = hilti::type::UnresolvedID(std::move($1)); } + ; + +type_param_begin: + '<' + { driver->disableExpressionMode(); } + +type_param_end: + '>' + { driver->enableExpressionMode(); } + +opt_type_flags: /* empty */ { $$ = hilti::type::Flags(); } + /* type_flag opt_type_flags { $$ = $1 + $2; } -- no type flags currently */ + +function_type : opt_func_flavor FUNCTION '(' opt_func_params ')' ARROW func_result + { $$ = hilti::type::Function($7, $4, $1, __loc__); } + ; + +tuple_type : TUPLE type_param_begin '*' type_param_end { $$ = hilti::type::Tuple(hilti::type::Wildcard(), __loc__); } + | TUPLE type_param_begin tuple_type_elems type_param_end { $$ = hilti::type::Tuple(std::move($3), __loc__); } + ; + +tuple_type_elems + : tuple_type_elems ',' tuple_type_elem + { $$ = std::move($1); $$.push_back(std::move($3)); } + | tuple_type_elem { $$ = std::vector>{ std::move($1) }; } + ; + +tuple_type_elem + : type { $$ = std::make_pair(hilti::ID(), std::move($1)); } + | local_id ':' type { $$ = std::make_pair(std::move($1), std::move($3)); } + ; + +struct_type : STRUCT opt_struct_params '{' struct_fields '}' { $$ = hilti::type::Struct(std::move($2), std::move($4), __loc__); } + +opt_struct_params + : '(' opt_func_params ')' { $$ = std::move($2); } + | /* empty */ { $$ = std::vector{}; } + +struct_fields : struct_fields struct_field { $$ = std::move($1); $$.push_back($2); } + | /* empty */ { $$ = std::vector{}; } + +struct_field : type local_id opt_attributes ';' { $$ = hilti::type::struct_::Field(std::move($2), std::move($1), std::move($3), __loc__); } + | func_flavor opt_func_cc func_result local_id '(' opt_func_params ')' opt_attributes ';' { + auto ftype = hilti::type::Function(std::move($3), std::move($6), $1, __loc__); + $$ = hilti::type::struct_::Field(std::move($4), $2, std::move(ftype), $8, __loc__); + } + +union_type : UNION opt_attributes'{' opt_union_fields '}' + { $$ = hilti::type::Union(std::move($4), __loc__); } + +opt_union_fields : union_fields { $$ = $1; } + | /* empty */ { $$ = std::vector(); } + +union_fields : union_fields ',' union_field { $$ = std::move($1); $$.push_back(std::move($3)); } + | union_field { $$ = std::vector(); $$.push_back(std::move($1)); } + +union_field : type local_id opt_attributes { $$ = hilti::type::union_::Field(std::move($2), std::move($1), std::move($3), __loc__); } + +enum_type : ENUM '{' enum_labels '}' { $$ = hilti::type::Enum(std::move($3), __loc__); } + | ENUM '<' '*' '>' { $$ = hilti::type::Enum(type::Wildcard(), __loc__); } + +enum_labels : enum_labels ',' enum_label { $$ = std::move($1); $$.push_back(std::move($3)); } + | enum_label { $$ = std::vector(); $$.push_back(std::move($1)); } + ; + +enum_label : local_id { $$ = hilti::type::enum_::Label(std::move($1), __loc__); } + | local_id '=' CUINTEGER { $$ = hilti::type::enum_::Label(std::move($1), $3, __loc__); } + ; + +/* Expressions */ + +expr : expr_0 { $$ = std::move($1); } + ; + +opt_exprs : exprs { $$ = std::move($1); } + | /* empty */ { $$ = std::vector(); } + +exprs : exprs ',' expr { $$ = std::move($1); $$.push_back(std::move($3)); } + | expr { $$ = std::vector{std::move($1)}; } + +expr_0 : expr_1 { $$ = std::move($1); } + ; + +expr_1 : expr_2 '=' expr_1 { $$ = hilti::expression::Assign(std::move($1), std::move($3), __loc__); } + | expr_2 MINUSASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DifferenceAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 PLUSASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::SumAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 TIMESASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::MultipleAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 DIVIDEASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DivisionAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 '?' expr_1 ':' expr_1 { $$ = hilti::expression::Ternary(std::move($1), std::move($3), std::move($5), __loc__); } + | expr_2 { $$ = std::move($1); } + +expr_2 : expr_2 OR expr_3 { $$ = hilti::expression::LogicalOr(std::move($1), std::move($3), __loc__); } + | expr_3 { $$ = std::move($1); } + +expr_3 : expr_3 AND expr_4 { $$ = hilti::expression::LogicalAnd(std::move($1), std::move($3), __loc__); } + | expr_4 { $$ = std::move($1); } + +expr_4 : expr_4 EQ expr_5 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Equal, {std::move($1), std::move($3)}, __loc__); } + | expr_4 NEQ expr_5 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Unequal, {std::move($1), std::move($3)}, __loc__); } + | expr_5 { $$ = std::move($1); } + +expr_5 : expr_5 '<' expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Lower, {std::move($1), std::move($3)}, __loc__); } + | expr_5 '>' expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Greater, {std::move($1), std::move($3)}, __loc__); } + | expr_5 GEQ expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::GreaterEqual, {std::move($1), std::move($3)}, __loc__); } + | expr_5 LEQ expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::LowerEqual, {std::move($1), std::move($3)}, __loc__); } + | expr_6 { $$ = std::move($1); } + +expr_6 : expr_6 '|' expr_7 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::BitOr, {std::move($1), std::move($3)}, __loc__); } + | expr_7 { $$ = std::move($1); } + +expr_7 : expr_7 '^' expr_8 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::BitXor, {std::move($1), std::move($3)}, __loc__); } + | expr_8 { $$ = std::move($1); } + +expr_8 : expr_8 '&' expr_9 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::BitAnd, {std::move($1), std::move($3)}, __loc__); } + | expr_9 { $$ = std::move($1); } + +expr_9 : expr_9 SHIFTLEFT expr_a { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::ShiftLeft, {std::move($1), std::move($3)}, __loc__); } + | expr_9 SHIFTRIGHT expr_a { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::ShiftRight, {std::move($1), std::move($3)}, __loc__); } + | expr_a { $$ = std::move($1); } + +expr_a : expr_a '+' expr_b { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Sum, {std::move($1), std::move($3)}, __loc__); } + | expr_a '-' expr_b { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Difference, {std::move($1), std::move($3)}, __loc__); } + | expr_b { $$ = std::move($1); } + +expr_b : expr_b '%' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Modulo, {std::move($1), std::move($3)}, __loc__); } + | expr_b '*' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Multiple, {std::move($1), std::move($3)}, __loc__); } + | expr_b '/' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Division, {std::move($1), std::move($3)}, __loc__); } + | expr_b POW expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Power, {std::move($1), std::move($3)}, __loc__); } + | expr_c { $$ = std::move($1); } + +expr_c : '!' expr_c { $$ = hilti::expression::LogicalNot(std::move($2), __loc__); } + | '*' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Deref, {std::move($2)}, __loc__); } + | '~' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Negate, {std::move($2)}, __loc__); } + | '|' expr_c '|' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Size, {std::move($2)}, __loc__); } + | MINUSMINUS expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DecrPrefix, {std::move($2)}, __loc__); } + | PLUSPLUS expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::IncrPrefix, {std::move($2)}, __loc__); } + | expr_d { $$ = std::move($1); } + +expr_d : expr_d '(' opt_exprs ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Call, {std::move($1), hilti::expression::Ctor(hilti::ctor::Tuple(std::move($3), __loc__))}, __loc__); } + | expr_d '.' member_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Member, {std::move($1), std::move($3)}, __loc__); } + | expr_d '.' member_expr '(' opt_exprs ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::MemberCall, {std::move($1), std::move($3), hilti::expression::Ctor(hilti::ctor::Tuple(std::move($5), __loc__))}, __loc__); } + | expr_d '[' expr ']' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Index, {std::move($1), std::move($3)}, __loc__); } + | expr_d HASATTR member_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::HasMember, {std::move($1), std::move($3)}, __loc__); } + | expr_d IN expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::In, {std::move($1), std::move($3)}, __loc__); } + | expr_d MINUSMINUS { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DecrPostfix, {std::move($1)}, __loc__); } + | expr_d PLUSPLUS { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::IncrPostfix, {std::move($1)}, __loc__); } + | expr_d TRYATTR member_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::TryMember, {std::move($1), std::move($3)}, __loc__); } + | expr_e { $$ = std::move($1); } + +expr_e : BEGIN_ '(' expr ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Begin, {std::move($3)}, __loc__); } + | CAST type_param_begin type type_param_end '(' expr ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Cast, {std::move($6), hilti::expression::Type_(std::move($3))}, __loc__); } + | END_ '(' expr ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::End, {std::move($3)}, __loc__); } + | MOVE '(' expr ')' { $$ = hilti::expression::Move(std::move($3), __loc__); } + | UNPACK type_param_begin type type_param_end tuple_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Unpack, {hilti::expression::Type_(std::move($3)), std::move($5)}, __loc__); } + | NEW expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::New, {std::move($2), hilti::expression::Ctor(hilti::ctor::Tuple({}, __loc__))}, __loc__); } + | NEW type { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::New, {hilti::expression::Type_(std::move($2)), hilti::expression::Ctor(hilti::ctor::Tuple({}, __loc__))}, __loc__); } + | NEW type '(' opt_exprs ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::New, {hilti::expression::Type_(std::move($2)), hilti::expression::Ctor(hilti::ctor::Tuple(std::move($4), __loc__))}, __loc__); } + | expr_f { $$ = std::move($1); } + +expr_f : ctor { $$ = hilti::expression::Ctor(std::move($1), __loc__); } + | '-' expr_g { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::SignNeg, {std::move($2)}, __loc__); } + | '[' expr FOR local_id IN expr ']' + { $$ = hilti::expression::ListComprehension(std::move($6), std::move($2), std::move($4), {}, __loc__); } + | '[' expr FOR local_id IN expr IF expr ']' + { $$ = hilti::expression::ListComprehension(std::move($6), std::move($2), std::move($4), std::move($8), __loc__); } + | expr_g + +expr_g : '(' expr ')' { $$ = std::move($2); } + | scoped_id { $$ = hilti::expression::UnresolvedID(std::move($1), __loc__); } + + +member_expr : local_id { $$ = hilti::expression::Member(std::move($1), __loc__); } + | ERROR { $$ = hilti::expression::Member(ID("error", __loc__), __loc__); } // allow methods of that name even though reserved keyword + +/* Constants */ + +ctor : CBOOL { $$ = hilti::ctor::Bool($1, __loc__); } + | CBYTES { $$ = hilti::ctor::Bytes(std::move($1), __loc__); } + | CSTRING { $$ = hilti::ctor::String($1, __loc__); } + | const_real { $$ = hilti::ctor::Real($1, __loc__); } + | CUINTEGER { $$ = hilti::ctor::UnsignedInteger($1, 64, __loc__); } + | '+' CUINTEGER { $$ = hilti::ctor::UnsignedInteger($2, 64, __loc__); } + | '-' CUINTEGER { if ( $2 > 0x8000000000000000 ) error(@$, "integer overflow on negation"); + $$ = hilti::ctor::SignedInteger(-$2, 64, __loc__); } + | CNULL { $$ = hilti::ctor::Null(__loc__); } + + | CADDRESS { $$ = hilti::ctor::Address(hilti::ctor::Address::Value($1), __loc__); } + | CADDRESS '/' CUINTEGER { $$ = hilti::ctor::Network(hilti::ctor::Network::Value($1, $3), __loc__); } + | CPORT { $$ = hilti::ctor::Port(hilti::ctor::Port::Value($1), __loc__); } + | INTERVAL '(' const_real ')' { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3), __loc__); } + | INTERVAL '(' const_sint ')' { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3), __loc__); } + | TIME '(' const_real ')' { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3), __loc__); } + | TIME '(' const_uint ')' { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3 * 1000000000), __loc__); } + | STREAM '(' CBYTES ')' { $$ = hilti::ctor::Stream(std::move($3), __loc__); } + + | ERROR '(' CSTRING ')' { $$ = hilti::ctor::Error(std::move($3), __loc__); } + | OPTIONAL '(' expr ')' { $$ = hilti::ctor::Optional(std::move($3), __loc__); } + | DEFAULT type_param_begin type type_param_end '(' opt_exprs ')' + { $$ = hilti::ctor::Default(std::move($3), std::move($6), __loc__); } + + | UINT '<' CUINTEGER '>' '(' CUINTEGER ')' + { $$ = hilti::ctor::UnsignedInteger($6, $3, __loc__); } + | INT '<' CUINTEGER '>' '(' CUINTEGER ')' + { $$ = hilti::ctor::SignedInteger($6, $3, __loc__); } + | INT '<' CUINTEGER '>' '(' '-' CUINTEGER ')' + { $$ = hilti::ctor::SignedInteger(-$7, $3, __loc__); } + + | list { $$ = std::move($1); } + | map { $$ = std::move($1); } + | regexp { $$ = std::move($1); } + | set { $$ = std::move($1); } + | struct_ { $$ = std::move($1); } + | tuple { $$ = std::move($1); } + ; + +const_real : CUREAL { $$ = $1; } + | '+' CUREAL { $$ = $2; } + | '-' CUREAL { $$ = -$2; } + +const_sint : CUINTEGER { $$ = $1; } + | '+' CUINTEGER { $$ = $2; } + | '-' CUINTEGER { if ( $2 > 0x8000000000000000 ) error(@$, "integer overflow on negation"); + $$ = -$2; + } + +const_uint : CUINTEGER { $$ = $1; } + | '+' CUINTEGER { $$ = $2; } + + +tuple : '(' opt_tuple_elems1 ')' { $$ = hilti::ctor::Tuple(std::move($2), __loc__); } + +opt_tuple_elems1 + : tuple_elem ',' opt_tuple_elems2 { $$ = std::vector{std::move($1)}; $$.insert($$.end(), $3.begin(), $3.end()); } + | /* empty */ { $$ = std::vector(); } + +opt_tuple_elems2 + : tuple_elem ',' opt_tuple_elems2 { $$ = std::vector{std::move($1)}; $$.insert($$.end(), $3.begin(), $3.end()); } + | tuple_elem { $$ = std::vector{ std::move($1)}; } + | /* empty */ { $$ = std::vector(); } + + +tuple_elem : expr { $$ = std::move($1); } + ; + +tuple_expr : tuple { $$ = hilti::expression::Ctor(std::move($1), __loc__); } + +list : '[' opt_exprs ']' { $$ = hilti::ctor::List(std::move($2), __loc__); } + | LIST '(' opt_exprs ')' { $$ = hilti::ctor::List(std::move($3), __loc__); } + | LIST type_param_begin type type_param_end '(' opt_tuple_elems1 ')' + { $$ = hilti::ctor::List(std::move($3), std::move($6), __loc__); } + | VECTOR '(' opt_exprs ')' { $$ = hilti::ctor::Vector(std::move($3), __loc__); } + | VECTOR type_param_begin type type_param_end '(' opt_tuple_elems1 ')' + { $$ = hilti::ctor::Vector(std::move($3), std::move($6), __loc__); } + +set : SET '(' opt_exprs ')' { $$ = hilti::ctor::Set(std::move($3), __loc__); } + | SET type_param_begin type type_param_end '(' opt_tuple_elems1 ')' + { $$ = hilti::ctor::Set(std::move($3), std::move($6), __loc__); } + +map : MAP '(' opt_map_elems ')' { $$ = hilti::ctor::Map(std::move($3), __loc__); } + | MAP type_param_begin type ',' type type_param_end '(' opt_map_elems ')' + { $$ = hilti::ctor::Map(std::move($3), std::move($5), std::move($8), __loc__); } + +struct_ : '[' struct_elems ']' { $$ = hilti::ctor::Struct(std::move($2), __loc__); } + +struct_elems : struct_elems ',' struct_elem { $$ = std::move($1); $$.push_back($3); } + | struct_elem { $$ = std::vector{ std::move($1) }; } + +struct_elem : '$' local_id '=' expr { $$ = hilti::ctor::struct_::Field(std::move($2), std::move($4)); } + +regexp : re_patterns opt_attributes { $$ = hilti::ctor::RegExp(std::move($1), std::move($2), __loc__); } + +re_patterns : re_patterns '|' re_pattern_constant + { $$ = $1; $$.push_back(std::move($3)); } + | re_pattern_constant { $$ = std::vector{std::move($1)}; } + +re_pattern_constant + : '/' { driver->enablePatternMode(); } CREGEXP { driver->disablePatternMode(); } '/' + { $$ = std::move($3); } + +opt_map_elems : map_elems { $$ = std::move($1); } + | /* empty */ { $$ = std::vector(); } + +map_elems : map_elems ',' map_elem { $$ = std::move($1); $$.push_back(std::move($3)); } + | map_elem { $$ = std::vector(); $$.push_back(std::move($1)); } + +map_elem : expr ':' expr { $$ = std::make_pair($1, $3); } + + +attribute : ATTRIBUTE { $$ = hilti::Attribute(std::move($1), __loc__); } + | ATTRIBUTE '=' expr { $$ = hilti::Attribute(std::move($1), std::move($3), __loc__); } + +opt_attributes + : opt_attributes attribute { $$ = hilti::AttributeSet::add($1, $2); } + | /* empty */ { $$ = {}; } + +%% + +void hilti::detail::parser::Parser::error(const Parser::location_type& l, const std::string& m) { + driver->error(m, toMeta(l)); +} diff --git a/hilti/src/compiler/parser/scanner.ll b/hilti/src/compiler/parser/scanner.ll new file mode 100644 index 000000000..3c0031a91 --- /dev/null +++ b/hilti/src/compiler/parser/scanner.ll @@ -0,0 +1,241 @@ +/* Copyright (c) 2020 by the Zeek Project. See LICENSE for details. */ + +%{ +#include + +#include +#include + +using token = hilti::detail::parser::Parser::token; +using token_type = hilti::detail::parser::Parser::token_type; + +using namespace hilti; +using namespace hilti::detail::parser; + +#define yyterminate() return token::EOD; + +%} + +%option c++ +%option prefix="Hilti" +%option noyywrap nounput batch debug yylineno + +%s EXPRESSION +%s IGNORE_NL + +%x DOTTED_ID +%x RE + +%{ +#define YY_USER_ACTION yylloc->columns(yyleng); + +static hilti::Meta toMeta(hilti::detail::parser::location l) { + return hilti::Meta(hilti::Location(*l.begin.filename, l.begin.line, l.end.line)); +} + +%} + +address4 ({digits}"."){3}{digits} +address6 ("["({hexs}:){7}{hexs}"]")|("["0x{hexs}({hexs}|:)*"::"({hexs}|:)*"]")|("["({hexs}|:)*"::"({hexs}|:)*"]")|("["({hexs}|:)*"::"({hexs}|:)*({digits}"."){3}{digits}"]") + +attribute \&[a-zA-Z_][a-zA-Z_0-9-]* +blank [ \t] +comment [ \t]*#[^\n]*\n +digit [0-9] +digits {digit}+ +hexit [0-9a-fA-F] +hexs {hexit}+ +E ([Ee][+-]?{digits}) +P ([Pp][+-]?{digits}) +decfloat {digits}{E}|{digit}*\.{digits}{E}?|{digits}\.{digit}*{E}? +hexfloat 0[xX]({hexit}+{P}|{hexit}*\.{hexit}+{P}?|{hexit}+\.{hexit}*{P}?) +id [a-zA-Z_]|[a-zA-Z_][a-zA-Z_0-9-]*[a-zA-Z_0-9]|[$][$] +property %[a-zA-Z_][a-zA-Z_0-9-]* +string \"(\\.|[^\\"])*\" + +%% + +%{ + auto range_error_int = [d=driver, l=yylloc] { d->error("integer literal range error", toMeta(*l)); }; + auto range_error_real = [d=driver, l=yylloc] { d->error("real literal range error", toMeta(*l)); }; + + yylloc->step (); +%} + +{blank}+ yylloc->step(); +[\n]+ yylloc->lines(yyleng); yylloc->step(); +{comment} yylloc->lines(1); yylloc->step(); + +__library_type return token::LIBRARY_TYPE; +addr return token::ADDRESS; +add return token::ADD; +any return token::ANY; +assert return token::ASSERT; +assert-exception return token::ASSERT_EXCEPTION; +auto return token::AUTO; +begin return token::BEGIN_; +bool return token::BOOL; +break return token::BREAK; +bytes return token::BYTES; +case return token::CASE; +cast return token::CAST; +catch return token::CATCH; +const return token::CONST; +const_iterator return token::CONST_ITERATOR; +continue return token::CONTINUE; +copy return token::COPY; +declare return token::DECLARE; +default return token::DEFAULT; +delete return token::DELETE; +else return token::ELSE; +end return token::END_; +enum return token::ENUM; +error return token::ERROR; +exception return token::EXCEPTION; +extern return token::EXTERN; +for return token::FOR; +from return token::FROM; +function return token::FUNCTION; +global return token::GLOBAL; +hook return token::HOOK; +if return token::IF; +import return token::IMPORT; +in return token::IN; +init return token::INIT; +inout return token::INOUT; +int return token::INT; +interval return token::INTERVAL; +iterator return token::ITERATOR; +list return token::LIST; +local return token::LOCAL; +map return token::MAP; +method return token::METHOD; +module return token::MODULE; +move return token::MOVE; +net return token::NETWORK; +new return token::NEW; +optional return token::OPTIONAL; +port return token::PORT; +private return token::PRIVATE; +public return token::PUBLIC; +real return token::REAL; +regexp return token::REGEXP; +result return token::RESULT; +return return token::RETURN; +set return token::SET; +stream return token::STREAM; +string return token::STRING; +strong_ref return token::STRONG_REF; +struct return token::STRUCT; +switch return token::SWITCH; +throw return token::THROW; +time return token::TIME; +try return token::TRY; +tuple return token::TUPLE; +type return token::TYPE; +uint return token::UINT; +union return token::UNION; +unpack return token::UNPACK; +value_ref return token::VALUE_REF; +vector return token::VECTOR; +view return token::VIEW; +void return token::VOID; +weak_ref return token::WEAK_REF; +while return token::WHILE; +yield return token::YIELD; + +!= return token::NEQ; +\&\& return token::AND; +\+= return token::PLUSASSIGN; +-- return token::MINUSMINUS; +-= return token::MINUSASSIGN; +\/= return token::DIVIDEASSIGN; +\*= return token::TIMESASSIGN; +\<\< return token::SHIFTLEFT; +\<= return token::LEQ; +== return token::EQ; +\>= return token::GEQ; +\?\. return token::HASATTR; +\.\? return token::TRYATTR; +\*\* return token::POW; +\+\+ return token::PLUSPLUS; +\|\| return token::OR; +\.\. return token::DOTDOT; +-> return token::ARROW; +\$\$ return token::DOLLARDOLLAR; +\>\> return token::SHIFTRIGHT; + + +\"C-HILTI\" yylval->str = std::string(yytext, 1, strlen(yytext) - 2); return token::CSTRING; +\"C\" yylval->str = std::string(yytext, 1, strlen(yytext) - 2); return token::CSTRING; + +Null return token::CNULL; + +False yylval->bool_ = false; return token::CBOOL; +True yylval->bool_ = true; return token::CBOOL; + +{digits}|0x{hexs} yylval->uint = util::chars_to_uint64(yytext, 0, range_error_int); return token::CUINTEGER; +'.' yylval->uint = *(yytext +1); return token::CUINTEGER; + +{decfloat}|{hexfloat} yylval->real = util::chars_to_double(yytext, range_error_real); return token::CUREAL; + +{string} yylval->str = util::expandEscapes(std::string(yytext, 1, strlen(yytext) - 2)); return token::CSTRING; +b{string} yylval->str = util::expandEscapes(std::string(yytext, 2, strlen(yytext) - 3)); return token::CBYTES; + +{digits}\/(tcp|udp) yylval->str = yytext; return token::CPORT; +{address4} yylval->str = yytext; return token::CADDRESS; +{address6} yylval->str = std::string(yytext, 1, strlen(yytext) - 2); return token::CADDRESS; + +{id} yylval->str = yytext; return token::IDENT; +{attribute} yylval->str = yytext; return token::ATTRIBUTE; +{property} yylval->str = yytext; return token::PROPERTY; +{id}(::{id}){1,} yylval->str = yytext; return token::SCOPED_IDENT; + +[][!$?.,=:;<>(){}/|*/&^%!+~-] return (token_type) yytext[0]; + +. driver->error("invalid character", toMeta(*yylloc)); + +(\\.|[^\\\/])* yylval->str = util::replace(yytext, "\\/", "/"); return token::CREGEXP; +[/\\\n] return (token_type) yytext[0]; + +{id}(\.{id})* yylval->str = yytext; return token::DOTTED_IDENT; +{blank}+ yylloc->step(); +[\n]+ yylloc->lines(yyleng); yylloc->step(); + +%% + +int HiltiFlexLexer::yylex() +{ + assert(false); // Shouldn't be called. + return 0; +} + +void hilti::detail::parser::Scanner::enablePatternMode() +{ + yy_push_state(RE); +} + +void hilti::detail::parser::Scanner::disablePatternMode() +{ + yy_pop_state(); +} + +void hilti::detail::parser::Scanner::enableExpressionMode() +{ + yy_push_state(EXPRESSION); +} + +void hilti::detail::parser::Scanner::disableExpressionMode() +{ + yy_pop_state(); +} + +void hilti::detail::parser::Scanner::enableDottedIDMode() +{ + yy_push_state(DOTTED_ID); +} + +void hilti::detail::parser::Scanner::disableDottedIDMode() +{ + yy_pop_state(); +} diff --git a/hilti/src/compiler/plugin.cc b/hilti/src/compiler/plugin.cc new file mode 100644 index 000000000..097da43cd --- /dev/null +++ b/hilti/src/compiler/plugin.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace hilti; +using namespace hilti::detail; + +PluginRegistry::PluginRegistry() = default; // Neded here to allow PluginRegistry to be forward declared. + +Result PluginRegistry::pluginForExtension(std::filesystem::path ext) const { + auto p = std::find_if(_plugins.begin(), _plugins.end(), [&](auto& p) { return p.extension == ext; }); + if ( p != _plugins.end() ) + return *p; + + return result::Error(util::fmt("no plugin registered for extension %s", ext)); +} + +PluginRegistry& plugin::registry() { + static PluginRegistry singleton; + return singleton; +} + +// Always-on default plugin with HILTI functionality. +static Plugin hilti_plugin() { + return Plugin{ + .component = "HILTI", + .extension = ".hlt", + .cxx_includes = {"hilti/rt/libhilti.h"}, + + .library_paths = + [](const std::shared_ptr& ctx) { return hilti::configuration().hilti_library_paths; }, + + .parse = [](std::istream& in, const std::filesystem::path& path) { return parseSource(in, path); }, + + .coerce_ctor = [](Ctor c, const Type& dst, + bitmask style) { return detail::coerceCtor(std::move(c), dst, style); }, + + .coerce_type = [](Type t, const Type& dst, + bitmask style) { return detail::coerceType(std::move(t), dst, style); }, + + .build_scopes = [](const std::shared_ptr& ctx, const std::vector>& m, + Unit* u) { buildScopes(m, u); }, + + .resolve_ids = [](const std::shared_ptr& ctx, Node* n, Unit* u) { return resolveIDs(n, u); }, + + .resolve_operators = [](const std::shared_ptr& ctx, Node* n, + Unit* u) { return resolveOperators(n, u); }, + + .apply_coercions = [](const std::shared_ptr& ctx, Node* n, + Unit* u) { return applyCoercions(n, u); }, + + .pre_validate = {}, + + .post_validate = [](const std::shared_ptr& ctx, const Node& n, Unit* u) { validateAST(n); }, + + .transform = {}, + }; +} + +static plugin::Register _(hilti_plugin()); diff --git a/hilti/src/compiler/unit.cc b/hilti/src/compiler/unit.cc new file mode 100644 index 000000000..90b1b9175 --- /dev/null +++ b/hilti/src/compiler/unit.cc @@ -0,0 +1,588 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace hilti; +using namespace hilti::context; +using util::fmt; + +namespace hilti::logging::debug { +inline const DebugStream Compiler("compiler"); +inline const DebugStream AstFinal("ast-final"); +inline const DebugStream AstOrig("ast-orig"); +inline const DebugStream AstResolved("ast-resolved"); +inline const DebugStream AstScopes("ast-scopes"); +inline const DebugStream AstPreTransformed("ast-pre-transformed"); +inline const DebugStream AstTransformed("ast-transformed"); +inline const DebugStream AstDumpIterations("ast-dump-iterations"); +} // namespace hilti::logging::debug + +template +bool runHooks(PluginMember hook, const std::string& debug_msg, const Args&... args) { + for ( const auto& p : plugin::registry().plugins() ) { + if ( ! (p.*hook) ) + continue; + + auto msg = debug_msg; + + if ( p.component != "HILTI" ) + msg += fmt(" (%s)", p.component); + + HILTI_DEBUG(logging::debug::Compiler, msg); + (*(p.*hook))(args...); + + if ( logger().errors() ) + return false; + } + + return true; +} + +template +bool runModifyingHooks(bool* modified, PluginMember hook, const std::string& debug_msg, const Args&... args) { + for ( const auto& p : plugin::registry().plugins() ) { + if ( ! (p.*hook) ) + continue; + + auto msg = debug_msg; + + if ( p.component != "HILTI" ) + msg += fmt(" (%s)", p.component); + + HILTI_DEBUG(logging::debug::Compiler, msg); + if ( (*(p.*hook))(args...) ) { + *modified = true; + HILTI_DEBUG(logging::debug::Compiler, " -> modified"); + } + + if ( logger().errors() ) + return false; + } + + return true; +} + +Result Unit::fromModule(const std::shared_ptr& context, hilti::Module&& module, + const std::filesystem::path& path) { + auto unit = Unit(context, module.id(), path, true); + auto cached = context->registerModule({unit.id(), path}, std::move(module), true); + unit._modules.insert(cached.index.id); + return unit; +} + +Result Unit::fromCache(const std::shared_ptr& context, const std::filesystem::path& path) { + auto cached = context->lookupModule(path); + if ( ! cached ) + return result::Error(fmt("unknown module %s", path)); + + auto unit = Unit(context, cached->index.id, cached->index.path, true); + unit._modules.insert(cached->index.id); + return unit; +} + +Result Unit::fromCache(const std::shared_ptr& context, const hilti::ID& id) { + auto cached = context->lookupModule(id); + if ( ! cached ) + return result::Error(fmt("unknown module %s", id)); + + auto unit = Unit(context, cached->index.id, cached->index.path, true); + unit._modules.insert(cached->index.id); + return unit; +} + +Result Unit::fromSource(const std::shared_ptr& context, const std::filesystem::path& path) { + auto module = Unit::parse(context, path); + if ( ! module ) + return module.error(); + + return fromModule(context, std::move(*module), path); +} + +Result Unit::fromCXX(std::shared_ptr context, detail::cxx::Unit cxx, const std::filesystem::path& path) { + auto unit = Unit(std::move(context), ID(fmt("", path.native())), path, false); + unit._cxx_unit = std::move(cxx); + // No entry in _modules. + return unit; +} + +Result Unit::parse(const std::shared_ptr& context, const std::filesystem::path& path) { + util::timing::Collector _("hilti/compiler/parser"); + + std::ifstream in; + in.open(path); + + if ( ! in ) + return result::Error(fmt("cannot open HILTI source file %s", path)); + + auto plugin = plugin::registry().pluginForExtension(path.extension()); + + if ( ! (plugin && plugin->parse) ) + return result::Error(fmt("no plugin provides support for importing *%s files", path.extension())); + + auto dbg_message = fmt("parsing file %s", path); + + if ( plugin->component != "HILTI" ) + dbg_message += fmt(" (%s)", plugin->component); + + HILTI_DEBUG(logging::debug::Compiler, dbg_message); + + auto module = (*plugin->parse)(in, path); + if ( ! module ) + return module.error(); + + return module->as(); +} + +Result Unit::compile() { + _dumpASTs(logging::debug::AstOrig, "Original AST"); + _saveIterationASTs("AST before first iteration"); + + int round = 1; + int extra_rounds = 0; // set to >0 for debugging + + while ( true ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("processing AST, round %d", round)); + logging::DebugPushIndent _(logging::debug::Compiler); + + bool modified = false; + + std::set performed_imports; + while ( true ) { + auto orig_modules = _modules; // _modules may be modified by importer pass + + for ( const auto& id : orig_modules ) { + if ( performed_imports.find(id) != performed_imports.end() ) + continue; + + auto cached = _context->lookupModule(id); + assert(cached); + + HILTI_DEBUG(logging::debug::Compiler, fmt("performing missing imports for module %s", id)); + { + logging::DebugPushIndent _(logging::debug::Compiler); + cached->dependencies = detail::importModules(*cached->node, this); + _context->updateModule(*cached); + performed_imports.insert(id); + } + } + + if ( logger().errors() ) + return result::Error("errors encountered during import"); + + if ( _modules.size() == orig_modules.size() ) + // repeat while as long as we keep adding modules + break; + } + + HILTI_DEBUG(logging::debug::Compiler, fmt("modules: %s", util::join(_modules, ", "))); + + auto modules = _currentModules(); + + for ( auto& [id, module] : modules ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("resetting nodes for module %s", id)); + detail::resetNodes(&*module); + } + + if ( ! runHooks(&Plugin::build_scopes, "building scopes for all module modules", context(), modules, this) ) + return result::Error("errors encountered during scope building"); + + _dumpASTs(logging::debug::AstScopes, "AST with scopes", round); + + if ( logger().errors() ) + return result::Error("errors encountered during scope building"); + + for ( auto& [id, module] : modules ) { + if ( ! runModifyingHooks(&modified, &Plugin::resolve_ids, fmt("resolving IDs in module %s", id), context(), + &*module, this) ) + return result::Error("errors encountered during ID resolving"); + } + + for ( auto& [id, module] : modules ) { + if ( ! runModifyingHooks(&modified, &Plugin::resolve_operators, fmt("resolving operators in module %s", id), + context(), &*module, this) ) + return result::Error("errors encountered during operator resolving"); + } + + for ( auto& [id, module] : modules ) { + if ( ! runModifyingHooks(&modified, &Plugin::apply_coercions, fmt("coercing expressions for %s", id), + context(), &*module, this) ) + return result::Error("errors encountered during expression coercion"); + } + + _dumpASTs(logging::debug::AstResolved, "AST after resolving", round); + + if ( plugin::registry().hasHookFor(&Plugin::transform) ) { + _dumpASTs(logging::debug::AstPreTransformed, "Pre-transformed AST", round); + + if ( ! options().skip_validation ) { + auto valid = true; + + for ( auto& [id, module] : modules ) + if ( ! runHooks(&Plugin::pre_validate, fmt("validating module %s (pre-transform)", id), context(), + *module, this) ) + valid = false; + + if ( ! valid ) + return result::Error("errors encountered during pre-transform validation"); + } + + for ( auto& [id, module] : modules ) { + if ( ! runModifyingHooks(&modified, &Plugin::transform, fmt("transforming module %s", id), context(), + &*module, round == 1, this) ) + return result::Error("errors encountered during source-to-source translation"); + } + + _dumpASTs(logging::debug::AstTransformed, "Transformed AST", round); + } + + if ( ! modified && extra_rounds-- == 0 ) + break; + + _saveIterationASTs("AST after iteration", round); + + if ( ++round >= 50 ) + logger().internalError("hilti::Unit::compile() didn't terminate, AST keeps changing"); + } + + auto& module = imported(_id); + + _dumpAST(module, logging::debug::AstFinal, "Final AST"); + _saveIterationASTs("Final AST", round); + + if ( ! options().skip_validation ) { + for ( auto& [id, module] : _currentModules() ) { + if ( const auto& p = module->as().preserved(); ! p.empty() ) { + if ( ! runHooks(&Plugin::preserved_validate, fmt("validating module %s (preserved)", id), context(), p, + this) ) + return result::Error("errors encountered during validation of preserved nodes"); + } + + if ( ! runHooks(&Plugin::post_validate, fmt("validating module %s (post-transform)", id), context(), + *module, this) ) + return result::Error("errors encountered during post-transform validation"); + } + } + + for ( auto& [id, module] : _currentModules() ) { + _determineCompilationRequirements(*module); + + // Cache the module's final state. + auto cached = _context->lookupModule(id); + cached->final = true; + _context->updateModule(*cached); + } + + return Nothing(); +} + +Result Unit::codegen() { + auto& module = imported(_id); + + HILTI_DEBUG(logging::debug::Compiler, fmt("compiling module %s to C++", _id)); + logging::DebugPushIndent _(logging::debug::Compiler); + + // Compile to C++. + auto c = detail::CodeGen(_context).compileModule(module, this); + + if ( logger().errors() ) + return result::Error("errors encountered during code generation"); + + if ( ! c ) + logger().internalError( + fmt("code generation for module %s failed, but did not log error (%s)", _id, c.error().description())); + + // Now compile the other modules to because we may need some of their + // declarations. + // + // TODO(robin): Would be nice if we had a "cheap" compilation mode + // that only generated declarations. + for ( auto& [id, module] : _currentModules() ) { + if ( id == _id ) + continue; + + HILTI_DEBUG(logging::debug::Compiler, fmt("importing declarations from module %s", id)); + auto other = detail::CodeGen(_context).compileModule(*module, this); + c->importDeclarations(*other); + } + + HILTI_DEBUG(logging::debug::Compiler, fmt("finalizing module %s", _id)); + if ( auto x = c->finalize(); ! x ) + return x.error(); + + _cxx_unit = *c; + return Nothing(); +} + +std::vector> Unit::_currentModules() const { + std::vector> modules; + + for ( const auto& id : _modules ) { + auto cached = _context->lookupModule(id); + assert(cached); + modules.emplace_back(id, NodeRef(cached->node)); + } + + return modules; +} + +std::optional Unit::_lookupModule(const ID& id) const { + if ( _modules.find(id) == _modules.end() ) + return {}; + + auto cached = _context->lookupModule(id); + assert(cached); + return cached; +} + +Result Unit::print(std::ostream& out) const { + detail::printAST(imported(_id), out); + return Nothing(); +} + +Result Unit::createPrototypes(std::ostream& out) { + if ( ! _cxx_unit ) + return result::Error("no C++ code available for unit"); + + return _cxx_unit->createPrototypes(out); +} + +Result Unit::cxxCode() const { + if ( ! _cxx_unit ) + return result::Error("no C++ code available for unit"); + + std::stringstream cxx; + _cxx_unit->print(cxx); + + if ( logger().errors() ) + return result::Error("errors during prototype creation"); + + return CxxCode{_cxx_unit->moduleID(), cxx}; +} + +Result Unit::import(const ID& id, const std::filesystem::path& ext, std::optional scope, + std::vector search_dirs) { + if ( auto cached = _lookupModule(id) ) + return cached->index; + + if ( auto cached = _context->lookupModule(id) ) { + _modules.insert(id); + return cached->index; + } + + auto plugin = plugin::registry().pluginForExtension(ext); + + if ( ! (plugin && plugin->parse) ) + return result::Error(fmt("no plugin provides support for importing *%s files", ext)); + + auto name = fmt("%s%s", util::tolower(id), ext.native()); + + if ( scope ) + name = fmt("%s/%s", util::replace(scope->str(), ".", "/"), name); + + std::vector library_paths = std::move(search_dirs); + + if ( plugin->library_paths ) + library_paths = util::concat(std::move(library_paths), (*plugin->library_paths)(context())); + + library_paths = util::concat(std::move(library_paths), options().library_paths); + + auto path = util::findInPaths(name, library_paths); + if ( ! path ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("Failed to find module '%s' in search paths:", name)); + for ( const auto& p : library_paths ) + HILTI_DEBUG(logging::debug::Compiler, fmt(" %s", p)); + + return result::Error(fmt("cannot find file")); + } + + return _import(*path, id); +} + +Result Unit::import(const std::filesystem::path& path) { + if ( auto cached = _context->lookupModule(path) ) { + _modules.insert(cached->index.id); + return cached->index; + } + + return _import(path, {}); +} + +Result Unit::_import(const std::filesystem::path& path, std::optional expected_name) { + auto module = parse(context(), path); + if ( ! module ) + return module.error(); + + auto id = module->id(); + + if ( expected_name && id != *expected_name ) + return result::Error(fmt("file %s does not contain expected module %s (but %s)", path, *expected_name, id)); + + HILTI_DEBUG(logging::debug::Compiler, fmt("loaded module %s from %s", id, path)); + + if ( auto cached = _lookupModule(id) ) + return cached->index; + + auto cached = context()->registerModule({id, path}, std::move(*module), false); + cached.dependencies = detail::importModules(*cached.node, this); + context()->updateModule(cached); + _modules.insert(id); + return cached.index; +} + +Node& Unit::imported(const ID& id) const { + if ( auto cached = _lookupModule(id) ) + return *cached->node; + else + throw std::out_of_range("no such module"); +} + +void Unit::_determineCompilationRequirements(const Node& module) { + // Visitor that goes over an AST and flags whether any node provides + // code that needs compilation. + struct VisitorModule : hilti::visitor::PreOrder { + explicit VisitorModule() = default; + result_t operator()(const declaration::GlobalVariable& n, const_position_t p) { return true; } + + result_t operator()(const declaration::Function& n, const_position_t p) { + return n.function().body() != std::nullopt; + } + }; + + // Visitor that extracts all imported modules from an AST and sets their + // requires-compilation flags. + struct VisitorImports : hilti::visitor::PreOrder { + explicit VisitorImports(std::shared_ptr ctx, const std::set& modules) + : context(std::move(ctx)), modules(modules) {} + std::shared_ptr context; + const std::set& modules; + + void operator()(const declaration::ImportedModule& n, const_position_t p) { + for ( const auto& i : p.node.scope()->items() ) { + for ( const auto& m : i.second ) { + if ( ! m->template isA() ) + continue; + + auto v = VisitorModule(); + for ( auto i : v.walk(*m) ) { + if ( auto x = v.dispatch(i); ! (x && *x) ) + continue; + + if ( auto cached = context->lookupModule(n.id()) ) { + cached->requires_compilation = true; + context->updateModule(*cached); + break; + } + } + } + } + } + }; + + // Run the visitors. + auto v = VisitorImports(context(), _modules); + for ( auto i : v.walk(module) ) + v.dispatch(i); +} + +void Unit::_dumpAST(const Node& module, const logging::DebugStream& stream, const std::string& prefix, int round) { + if ( ! logger().isEnabled(stream) ) + return; + + const auto& m = module.as(); + + std::string r; + + if ( round > 0 ) + r = fmt(" (round %d)", round); + + HILTI_DEBUG(stream, fmt("# %s: %s%s", m.id(), prefix, r)); + detail::renderNode(module, stream, true); + + if ( m.preserved().size() ) { + HILTI_DEBUG(stream, fmt("# %s: Preserved nodes%s", m.id(), r)); + for ( const auto& i : m.preserved() ) + detail::renderNode(i, stream, true); + } +} + +void Unit::_dumpASTs(const logging::DebugStream& stream, const std::string& prefix, int round) { + if ( ! logger().isEnabled(stream) ) + return; + + for ( auto& [id, module] : _currentModules() ) + _dumpAST(*module, stream, prefix, round); +} + +void Unit::_dumpAST(const Node& module, std::ostream& stream, const std::string& prefix, int round) { + const auto& m = module.as(); + + std::string r; + + if ( round > 0 ) + r = fmt(" (round %d)", round); + + stream << fmt("# %s: %s%s\n", m.id(), prefix, r); + detail::renderNode(module, stream, true); + + if ( m.preserved().size() ) { + stream << fmt("# %s: Preserved nodes%s\n", m.id(), r); + for ( const auto& i : m.preserved() ) + detail::renderNode(i, stream, true); + } +} + +void Unit::_dumpASTs(std::ostream& stream, const std::string& prefix, int round) { + for ( auto& [id, module] : _currentModules() ) + _dumpAST(*module, stream, prefix, round); +} + +void Unit::_saveIterationASTs(const std::string& prefix, int round) { + if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) + return; + + std::ofstream out(fmt("ast-%d.tmp", round)); + _dumpASTs(out, prefix, round); +} + +Result Unit::link(const std::shared_ptr& context, const std::vector& mds) { + HILTI_DEBUG(logging::debug::Compiler, fmt("linking %u modules", mds.size())); + auto cxx_unit = detail::CodeGen(context).linkUnits(mds); + + if ( ! cxx_unit ) + return result::Error("no C++ code available for unit"); + + return fromCXX(context, *cxx_unit, ""); +} + +std::pair> Unit::readLinkerMetaData(std::istream& input, + const std::filesystem::path& path) { + HILTI_DEBUG(logging::debug::Compiler, fmt("reading linker data from %s", path)); + return detail::cxx::Unit::readLinkerMetaData(input); +} + +std::set Unit::allImported(bool code_only) const { + std::set all; + + for ( const auto& m : _modules ) { + auto cached = _lookupModule(m); + assert(cached); + + if ( code_only && ! cached->requires_compilation ) + continue; + + all.insert(cached->index); + } + + return all; +} diff --git a/hilti/src/compiler/visitors/apply-coercions.cc b/hilti/src/compiler/visitors/apply-coercions.cc new file mode 100644 index 000000000..22f68d567 --- /dev/null +++ b/hilti/src/compiler/visitors/apply-coercions.cc @@ -0,0 +1,369 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +namespace { + +struct Visitor : public visitor::PreOrder { + Visitor(Unit* unit) : unit(unit) {} + Unit* unit; + bool modified = false; + + /** Returns a method call's i-th argument. */ + auto methodArgument(const expression::ResolvedOperatorBase& o, int i) { + auto ctor = o.op2().as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return ctor.as().value()[i]; + } + +#if 0 + void preDispatch(const Node& n, int level) override { + auto indent = std::string(level * 2, ' '); + std::cerr << "# " << indent << "> " << n.render() << std::endl; + n.scope()->render(std::cerr, " | "); + }; +#endif + + template + void replaceNode(position_t* p, T&& n) { + p->node = std::forward(n); + modified = true; + } + + /** Coerces an expression to a given type, recording an error if not possible. */ + std::optional coerceTo(Node* n, const Expression& e, const Type& t, bool contextual, bool assignment) { + if ( t == type::unknown ) + return {}; + + bitmask style = + (assignment ? CoercionStyle::TryAllForAssignment : CoercionStyle::TryAllForMatching); + + if ( contextual ) + style |= CoercionStyle::ContextualConversion; + + if ( auto c = hilti::coerceExpression(e, t, style) ) + return c.nexpr; + + n->setError(fmt("cannot coerce expression '%s' of type '%s' to type '%s'", e, e.type(), t)); + return {}; + } + + Result>> coerceCallArguments(Node* n, std::vector exprs, + std::vector params) { + // Build a tuple to coerce expression according to an OperandList. + auto src = expression::Ctor(ctor::Tuple(exprs)); + auto dst = type::OperandList::fromParameters(params); + + auto coerced = coerceExpression(src, type::constant(dst), CoercionStyle::TryAllForFunctionCall); + if ( ! coerced ) { + auto src_types = util::join(util::transform(exprs, [&](auto e) { return fmt("%s", e.type()); }), ", "); + auto dst_types = util::join(util::transform(dst.operands(), [&](auto o) { return fmt("%s", o); }), ", "); + n->setError(fmt("cannot coerce arguments '%s' of types '%s' to parameters '%s'", Expression(src), src_types, + dst_types)); + return result::Error("coercion failed"); + } + + if ( ! coerced.nexpr ) + // No change. + return {std::nullopt}; + + return {coerced.nexpr->as().ctor().as().value()}; + } + + void operator()(const Attribute& n) { + // TODO(robin): Coerce attributes with expressions. + } + + void operator()(const ctor::Default& n, position_t p) { + if ( auto stype = n.type().tryAs() ) { + if ( auto x = n.typeArguments(); x.size() ) { + if ( auto coerced = coerceCallArguments(&p.node, x, stype->parameters()); coerced && *coerced ) { + auto m = ctor::Default::setTypeArguments(n, **coerced); + replaceNode(&p, std::move(m)); + } + } + } + } + + void operator()(const declaration::Parameter& n, position_t p) { + if ( auto def = n.default_(); def && def->type() != n.type() ) + if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) { + auto m = declaration::Parameter::setDefault(n, *x); + replaceNode(&p, std::move(m)); + } + } + + void operator()(const declaration::LocalVariable& n, position_t p) { + std::optional init; + std::optional> args; + + if ( auto def = n.init(); def && def->type() != n.type() ) { + if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) + init = std::move(*x); + } + + if ( auto stype = n.type().tryAs() ) { + if ( ! n.typeArguments().empty() ) { + if ( auto coerced = coerceCallArguments(&p.node, n.typeArguments(), stype->parameters()); + coerced && *coerced ) + args = std::move(*coerced); + } + } + + if ( init || args ) { + Declaration new_ = n; + + if ( init ) + new_ = declaration::LocalVariable::setInit(new_.as(), std::move(*init)); + + if ( args ) + new_ = declaration::LocalVariable::setTypeArguments(new_.as(), + std::move(*args)); + + replaceNode(&p, std::move(new_)); + } + } + + void operator()(const declaration::GlobalVariable& n, position_t p) { + std::optional init; + std::optional> args; + + if ( auto def = n.init(); def && def->type() != n.type() ) { + if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) + init = std::move(*x); + } + + if ( auto stype = n.type().tryAs() ) { + if ( auto x = n.typeArguments(); x.size() ) { + if ( auto coerced = coerceCallArguments(&p.node, x, stype->parameters()); coerced && *coerced ) + args = std::move(*coerced); + } + } + + if ( init || args ) { + Declaration new_ = n; + + if ( init ) + new_ = declaration::GlobalVariable::setInit(new_.as(), std::move(*init)); + + if ( args ) + new_ = declaration::GlobalVariable::setTypeArguments(new_.as(), + std::move(*args)); + + replaceNode(&p, std::move(new_)); + } + } + + void operator()(const operator_::generic::New& n, position_t p) { + if ( auto etype = n.op0().tryAs() ) { + if ( auto stype = etype->typeValue().tryAs() ) { + auto args = n.op1().as().ctor().as().value(); + if ( auto coerced = coerceCallArguments(&p.node, args, stype->parameters()); coerced && *coerced ) { + Expression ntuple = expression::Ctor(ctor::Tuple(**coerced), n.op1().meta()); + auto nop = expression::resolved_operator::setOp1(n, std::move(ntuple)); + replaceNode(&p, nop); + } + } + } + } + + void operator()(const operator_::vector::PushBack& n, position_t p) { + // Need to coerce the element here as the normal overload resolution + // couldn't know the element type yet. + auto etype = type::effectiveType(n.op0().type()).as().elementType(); + auto elem = methodArgument(n, 0); + + if ( etype != elem.type() ) { + if ( auto x = coerceTo(&p.node, n.op2(), type::Tuple({etype}), false, true) ) { + auto nop = expression::resolved_operator::setOp2(n, *x); + replaceNode(&p, nop); + } + } + } + + void operator()(const statement::Assert& n, position_t p) { + if ( ! n.expectsException() && n.expression().type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, n.expression(), type::Bool(), true, false) ) { + auto m = statement::Assert::setCondition(n, *x); + replaceNode(&p, std::move(m)); + } + } + } + + void operator()(const statement::If& n, position_t p) { + if ( n.condition() ) { + if ( n.condition()->type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, *n.condition(), type::Bool(), true, false) ) { + auto m = statement::If::setCondition(n, *x); + replaceNode(&p, std::move(m)); + } + } + } + + else { + auto init = (*n.init()).as(); + Expression ncond = expression::UnresolvedID(init.id()); + Statement nif = statement::If::setCondition(n, ncond); + replaceNode(&p, std::move(nif)); + } + } + + void operator()(const statement::Return& n, position_t p) { + if ( auto func = p.findParent() ) { + if ( auto e = n.expression(); e && e->type() != func->get().type().result().type() ) { + if ( auto x = coerceTo(&p.node, *e, func->get().type().result().type(), false, true) ) { + auto m = statement::Return::setExpression(n, *x); + replaceNode(&p, std::move(m)); + } + } + } + else + p.node.setError("return outside of function"); + } + + void operator()(const statement::While& n, position_t p) { + if ( n.condition() ) { + if ( n.condition()->type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, *n.condition(), type::Bool(), true, false) ) { + auto m = statement::While::setCondition(n, *x); + replaceNode(&p, std::move(m)); + } + } + } + + else { + auto init = (*n.init()).as(); + auto ninit = declaration::LocalVariable::setInit(init, {}).as(); + ninit = declaration::LocalVariable::setType(ninit, init.type()).as(); + Expression ncond = expression::Assign(expression::UnresolvedID(init.id()), *init.init()); + + if ( ncond.type() != type::Bool() && ncond.type() != type::unknown ) { + if ( auto x = coerceTo(&p.node, ncond, type::Bool(), true, false) ) { + ncond = builder::equal(ncond, builder::bool_(true)); + auto nwhile = statement::While::setInit(n, ninit).as(); + nwhile = statement::While::setCondition(nwhile, ncond).as(); + replaceNode(&p, nwhile); + } + } + } + } + + void operator()(const type::struct_::Field& f, position_t p) { + if ( auto attrs = f.attributes() ) { + if ( auto x = attrs->coerceValueTo("&default", f.type()) ) { + if ( *x ) { + auto nattrs = type::struct_::Field::setAttributes(f, *attrs); + replaceNode(&p, std::move(nattrs)); + } + + return; + } + else + p.node.setError(fmt("cannot coerce default expression to type '%s'", f.type())); + } + } + + void operator()(const expression::Assign& n, position_t p) { + if ( n.source().type() != n.target().type() ) { + // We allow assignments from const to non-const here, assignment + // is by value. + if ( auto x = coerceTo(&p.node, n.source(), n.target().type(), false, true) ) { + auto m = expression::Assign::setSource(n, *x); + replaceNode(&p, std::move(m)); + } + } + } + + void operator()(const expression::LogicalAnd& n, position_t p) { + expression::LogicalAnd nn = n; + bool changed = false; + + if ( n.op0().type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, n.op0(), type::Bool(), true, false) ) { + nn = expression::LogicalAnd::setOp0(nn, *x).as(); + changed = true; + } + } + + if ( n.op1().type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, n.op1(), type::Bool(), true, false) ) { + nn = expression::LogicalAnd::setOp1(nn, *x).as(); + changed = true; + } + } + + if ( changed ) + replaceNode(&p, std::move(nn)); + } + + void operator()(const expression::LogicalNot& n, position_t p) { + if ( n.expression().type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, n.expression(), type::Bool(), true, false) ) { + auto m = expression::LogicalNot::setExpression(n, *x); + replaceNode(&p, std::move(m)); + } + } + } + + void operator()(const expression::LogicalOr& n, position_t p) { + expression::LogicalOr nn = n; + bool changed = false; + + if ( n.op0().type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, n.op0(), type::Bool(), true, false) ) { + nn = expression::LogicalOr::setOp0(nn, *x).as(); + changed = true; + } + } + + if ( n.op1().type() != type::Bool() ) { + if ( auto x = coerceTo(&p.node, n.op1(), type::Bool(), true, false) ) { + nn = expression::LogicalOr::setOp1(nn, *x).as(); + changed = true; + } + } + + if ( changed ) + replaceNode(&p, std::move(nn)); + } + + void operator()(const expression::PendingCoerced& pc, position_t p) { + if ( auto ner = hilti::coerceExpression(pc.expression(), pc.type()); ner.coerced ) { + if ( ner.nexpr ) { + // A coercion expression was created, use it. + p.node = *ner.nexpr; + modified = true; + } + else { + // Coercion not needed, use original expression. + p.node = pc.expression(); + modified = true; + } + } + else + p.node.setError(fmt("cannot coerce expression '%s' to type '%s'", pc.expression(), pc.type())); + } +}; + +} // anonymous namespace + +bool hilti::detail::applyCoercions(Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/apply-coercions"); + + auto v = Visitor(unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); + return v.modified; +} diff --git a/hilti/src/compiler/visitors/coercer.cc b/hilti/src/compiler/visitors/coercer.cc new file mode 100644 index 000000000..5990a3607 --- /dev/null +++ b/hilti/src/compiler/visitors/coercer.cc @@ -0,0 +1,572 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; + +namespace { + +struct VisitorCtor : public visitor::PreOrder, VisitorCtor> { + VisitorCtor(const Type& dst, bitmask style) : dst(dst), style(style) {} + + const Type& dst; + bitmask style; + + result_t operator()(const ctor::Enum& c) { + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return ctor::Bool(c.value().id() != ID("Undef"), c.meta()); + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Map& c) { + if ( auto t = dst.tryAs() ) { + std::vector nelemns; + for ( const auto& e : c.value() ) { + auto k = hilti::coerceExpression(e.first, t->keyType(), style); + auto v = hilti::coerceExpression(e.second, t->elementType(), style); + + if ( k && v ) + nelemns.emplace_back(*k.coerced, *v.coerced); + else + return {}; // FIXME(bbannier): issue for void? + } + + return ctor::Map(t->keyType(), t->elementType(), nelemns, c.meta()); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Null& c) { + if ( auto t = dst.tryAs() ) + return ctor::Optional(t->dereferencedType()); + + if ( auto t = dst.tryAs() ) + return ctor::StrongReference(t->dereferencedType()); + + if ( auto t = dst.tryAs() ) + return ctor::WeakReference(t->dereferencedType()); + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::List& c) { + if ( auto t = dst.tryAs() ) { + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, t->elementType(), CoercionStyle::TryAllForAssignment) ) + nexprs.push_back(*x.coerced); + else + return {}; // FIXME(bbannier): issue for void? + } + return ctor::List(t->elementType(), std::move(nexprs), c.meta()); + } + + if ( auto t = dst.tryAs() ) { + auto dt = t->isWildcard() ? c.elementType() : t->elementType(); + + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, dt, CoercionStyle::TryAllForAssignment) ) + nexprs.push_back(*x.coerced); + else + return {}; // FIXME(bbannier): issue for void? + } + return ctor::Vector(dt, std::move(nexprs), c.meta()); + } + + if ( auto t = dst.tryAs() ) { + auto dt = t->isWildcard() ? c.elementType() : t->elementType(); + + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, dt, CoercionStyle::TryAllForAssignment) ) + nexprs.push_back(*x.coerced); + else + return {}; // FIXME(bbannier): issue for void? + } + return ctor::Set(dt, std::move(nexprs), c.meta()); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Real& c) { + // Note: double->Integral constant conversions check 'non-narrowing' via + // double->Int->double roundtrip - the generated code looks good. + + if ( auto t = dst.tryAs() ) { + double d = c.value(); + + if ( double(int64_t(d)) == d ) { + switch ( t->isWildcard() ? 64 : t->width() ) { + case 8: + if ( double(int8_t(d)) == d ) + return ctor::SignedInteger(int64_t(d), 8, c.meta()); + break; + + case 16: + if ( double(int16_t(d)) == d ) + return ctor::SignedInteger(int64_t(d), 16, c.meta()); + break; + + case 32: + if ( double(int32_t(d)) == d ) + return ctor::SignedInteger(int64_t(d), 32, c.meta()); + break; + + case 64: return ctor::SignedInteger(int64_t(d), 64, c.meta()); break; + } + } + } + + if ( auto t = dst.tryAs() ) { + double d = c.value(); + + if ( double(uint64_t(d)) == d ) { + switch ( t->isWildcard() ? 64 : t->width() ) { + case 8: + if ( double(uint8_t(d)) == d ) + return ctor::UnsignedInteger(uint64_t(d), 8, c.meta()); + break; + + case 16: + if ( double(uint16_t(d)) == d ) + return ctor::UnsignedInteger(uint64_t(d), 16, c.meta()); + break; + + case 32: + if ( double(uint32_t(d)) == d ) + return ctor::UnsignedInteger(uint64_t(d), 32, c.meta()); + break; + + case 64: return ctor::UnsignedInteger(uint64_t(d), 64, c.meta()); break; + } + } + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Set& c) { + if ( auto t = dst.tryAs() ) { + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, t->elementType(), style) ) + nexprs.push_back(*x.coerced); + else + return {}; // FIXME(bbannier): issue for void? + } + return ctor::Set(t->elementType(), std::move(nexprs), c.meta()); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::SignedInteger& c) { + if ( auto t = dst.tryAs() ) { + if ( t->width() == 64 ) + return c; + + int64_t i = c.value(); + + if ( t->isWildcard() ) + return ctor::SignedInteger(i, c.type().width(), c.meta()); + + if ( auto [imin, imax] = util::signed_integer_range(t->width()); i >= imin && i <= imax ) + return ctor::SignedInteger(i, t->width(), c.meta()); + } + + if ( auto t = dst.tryAs(); t && c.value() >= 0 ) { + auto u = static_cast(c.value()); + + if ( t->isWildcard() ) + return ctor::UnsignedInteger(u, c.type().width(), c.meta()); + + if ( auto [zero, umax] = util::unsigned_integer_range(t->width()); u <= umax ) + return ctor::UnsignedInteger(u, t->width(), c.meta()); + } + + if ( auto t = dst.tryAs() ) { + if ( int64_t(double(c.value())) == c.value() ) + return ctor::Real(double(c.value())); + } + + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return ctor::Bool(c.value() != 0, c.meta()); + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Vector& c) { + if ( auto t = dst.tryAs() ) { + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, t->elementType(), style) ) + nexprs.push_back(*x.coerced); + else + return {}; // FIXME(bbannier): issue for void? + } + return ctor::Vector(t->elementType(), std::move(nexprs), c.meta()); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::UnsignedInteger& c) { + if ( auto t = dst.tryAs() ) { + if ( t->width() == 64 ) + return c; + + uint64_t u = c.value(); + + if ( t->isWildcard() ) + return ctor::UnsignedInteger(u, c.type().width(), c.meta()); + + if ( auto [umin, umax] = util::unsigned_integer_range(t->width()); u >= umin && u <= umax ) + return ctor::UnsignedInteger(u, t->width(), c.meta()); + } + + if ( auto t = dst.tryAs(); t && static_cast(c.value()) >= 0 ) { + auto i = static_cast(c.value()); + + if ( t->isWildcard() ) + return ctor::SignedInteger(i, c.type().width(), c.meta()); + + if ( auto [imin, imax] = util::signed_integer_range(t->width()); i >= imin && i <= imax ) + return ctor::SignedInteger(i, t->width(), c.meta()); + } + + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return ctor::Bool(c.value() != 0, c.meta()); + + if ( auto t = dst.tryAs() ) { + if ( uint64_t(double(c.value())) == c.value() ) + return ctor::Real(double(c.value())); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Tuple& c) { + if ( auto t = dst.tryAs() ) { + auto vc = c.value(); + auto vt = t.value().types(); + + if ( vc.size() != vt.size() ) + return {}; // FIXME(bbannier): issue for void? + + std::vector coerced; + coerced.reserve(vc.size()); + + for ( auto i = std::make_pair(vc.cbegin(), vt.cbegin()); i.first != vc.cend(); ++i.first, ++i.second ) { + if ( auto x = hilti::coerceExpression(*i.first, *i.second, CoercionStyle::TryAllForAssignment) ) { + coerced.push_back(*x.coerced); + } + else + return {}; // FIXME(bbannier): issue for void? + } + + return ctor::Tuple(std::move(coerced), c.meta()); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const ctor::Struct& c) { + auto dst_ = dst; + + if ( (dst.isA() || dst.isA()) && ! type::isReferenceType(dst) ) + // Allow coercion from value to reference type with new instance. + dst_ = dst.dereferencedType(); + + if ( auto dtype = dst_.tryAs() ) { + auto stype = c.type().as(); + + std::set src_fields; + for ( const auto& f : stype.fields() ) + src_fields.insert(f.id()); + + std::set dst_fields; + for ( const auto& f : dtype->fields() ) + dst_fields.insert(f.id()); + + // Check for fields in ctor that type does not have. + if ( ! util::set_difference(src_fields, dst_fields).empty() ) + return {}; // FIXME(bbannier): issue for void? + + // Check for fields in type that ctor does not have, they must be + // optional, + auto x = util::set_difference(dst_fields, src_fields); + + std::set can_be_missing; + + for ( const auto& k : x ) { + auto f = dtype->field(k); + if ( f->isOptional() || f->default_() || f->type().isA() ) + can_be_missing.insert(k); + } + + x = util::set_difference(x, can_be_missing); + + if ( ! x.empty() ) + // Uninitialized fields. + return {}; // FIXME(bbannier): issue for void? + + // Coerce each field. + std::vector nf; + + for ( const auto& sf : stype.fields() ) { + auto df = dtype->field(sf.id()); + auto se = c.field(sf.id()); + assert(df && se); + if ( auto ne = hilti::coerceExpression((*se).second, df->type(), style) ) + nf.emplace_back(sf.id(), *ne.coerced); + else + // Cannot coerce. + return {}; // FIXME(bbannier): issue for void? + } + + return ctor::Struct(std::move(nf), *dtype, c.meta()); + } + + return {}; // FIXME(bbannier): issue for void? + } +}; + +struct VisitorType : public visitor::PreOrder, VisitorType> { + VisitorType(const Type& dst, bitmask style) : dst(dst), style(style) {} + + const Type& dst; + bitmask style; + + result_t operator()(const type::Enum& c) { + if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Null& c) { + if ( auto t = dst.tryAs() ) + return dst; + + if ( auto t = dst.tryAs() ) + return dst; + + if ( auto t = dst.tryAs() ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Bytes& c) { + if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Error& e) { + if ( auto t = dst.tryAs() ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::List& e) { + if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) + return dst; + + if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Optional& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::StrongReference& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + if ( type::isReferenceType(dst) ) { + if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) + return dst; + } + + if ( ! (style & CoercionStyle::Assignment) ) { + if ( r.dereferencedType() == dst ) + return dst; + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Result& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + if ( auto t = dst.tryAs(); t && t->dereferencedType() == r.dereferencedType() ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::SignedInteger& src) { + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return dst; + + if ( auto t = dst.tryAs() ) { + if ( src.width() <= t->width() ) + return dst; + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Stream& c) { + if ( auto t = dst.tryAs() ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::stream::View& c) { + if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Type_& src) { + if ( auto t = dst.tryAs() ) { + // We don't allow arbitrary coercions here, just (more or less) direct matches. + if ( auto x = hilti::coerceType(src.typeValue(), t->typeValue(), CoercionStyle::TryDirectForMatching) ) + return type::Type_(*x); + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Union& c) { + if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::UnsignedInteger& src) { + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return dst; + + if ( auto t = dst.tryAs() ) { + if ( src.width() <= t->width() ) + return dst; + } + + if ( auto t = dst.tryAs() ) { + // As long as the target type has more bits, we can coerce. + if ( src.width() < t->width() ) + return dst; + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::Tuple& src) { + if ( auto t = dst.tryAs() ) { + auto vc = src.types(); + auto vt = t->types(); + + if ( vc.size() != vt.size() ) + return {}; // FIXME(bbannier): issue for void? + + for ( auto i = std::make_pair(vc.cbegin(), vt.cbegin()); i.first != vc.cend(); ++i.first, ++i.second ) { + if ( auto x = hilti::coerceType(*i.first, *i.second); ! x ) + return {}; // FIXME(bbannier): issue for void? + } + + return dst; + } + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::ValueReference& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return hilti::coerceType(r.dereferencedType(), dst, style); + + if ( type::isReferenceType(dst) ) { + if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) + return dst; + } + + if ( r.dereferencedType() == dst ) + return dst; + + return {}; // FIXME(bbannier): issue for void? + } + + result_t operator()(const type::WeakReference& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + if ( type::isReferenceType(dst) ) { + if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) + return dst; + } + + if ( ! (style & CoercionStyle::Assignment) ) { + if ( r.dereferencedType() == dst ) + return dst; + } + + return {}; // FIXME(bbannier): issue for void? + } +}; + +} // anonymous namespace + +// Plugin-specific version just kicking off the local visitor. +std::optional detail::coerceCtor(Ctor c, const Type& dst, bitmask style) { + if ( auto nc = VisitorCtor(dst, style).dispatch(std::move(c)) ) + return *nc; + + return {}; +} + +// Plugin-specific version just kicking off the local visitor. +std::optional detail::coerceType(Type t, const Type& dst, bitmask style) { + if ( auto nt = VisitorType(dst, style).dispatch(std::move(t)) ) + return *nt; + + return {}; +} diff --git a/hilti/src/compiler/visitors/id-resolver.cc b/hilti/src/compiler/visitors/id-resolver.cc new file mode 100644 index 000000000..883ca3f14 --- /dev/null +++ b/hilti/src/compiler/visitors/id-resolver.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; + +namespace { + +struct Visitor : public visitor::PreOrder { + explicit Visitor(Unit* unit) : unit(unit) {} + Unit* unit; + ID module_id = ID(""); + bool modified = false; + + template + void replaceNode(position_t* p, T&& n, bool set_modified = true) { + p->node = std::forward(n); + if ( set_modified ) + modified = true; + } + +#if 0 + void preDispatch(const Node& n, int level) override { + auto indent = std::string(level * 2, ' '); + std::cerr << "# " << indent << "> " << n.render() << std::endl; + n.scope()->render(std::cerr, " | "); + } +#endif + + void operator()(const Module& m) { module_id = m.id(); } + + void operator()(const type::UnresolvedID& u, position_t p) { + auto resolved = lookupID(u.id(), p); + + if ( ! resolved ) { + p.node.setError(resolved.error()); + return; + } + + Type t = type::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta()); + + if ( resolved->first->as().isOnHeap() ) { + // TODO(robin): This logic is pretty brittle as we need make sure + // to skip the transformation for certain AST nodes. Not sure how + // to improve this. + auto pc = p.parent().tryAs(); + auto pe = p.parent().tryAs(); + auto pt = p.parent().tryAs(); + + auto replace = true; + + if ( pt && type::isReferenceType(*pt) ) + replace = false; + + if ( pc && type::isReferenceType(pc->type()) ) + replace = false; + + if ( pc && pc->isA() ) + replace = false; + + if ( pe && pe->isA() ) + replace = false; + + if ( pe && pe->isA() ) { + if ( pe->isA() ) + replace = false; + + if ( pe->isA() ) + replace = false; + + if ( pe->isA() ) + replace = false; + } + + if ( pe && pe->isA() ) { + if ( pe->as().kind() == operator_::Kind::Deref ) + replace = false; + } + + if ( replace ) + t = type::ValueReference(t, Location("")); + } + + replaceNode(&p, t); + } + + void operator()(const type::Computed& u, position_t p) { + // As soon as we now the computed type, we swap it in. + if ( auto t = u.type(); ! t.isA() ) { + if ( auto id = t.typeID() ) + replaceNode(&p, type::UnresolvedID(*id, p.node.meta())); + else + replaceNode(&p, t); + } + } + + void operator()(const expression::UnresolvedID& u, position_t p) { + auto resolved = lookupID(u.id(), p); + + if ( ! resolved ) { + p.node.setError(resolved.error()); + return; + } + + if ( auto t = resolved->first->tryAs() ) { + auto nt = type::setTypeID(t->type(), resolved->second); + if ( ! t->typeID() ) + *resolved->first = declaration::Type::setType(*t, nt); + + replaceNode(&p, expression::Type_(nt, u.meta())); + return; + } + + // If we are inside a call expression, leave it alone. The operator + // resolver will take care of that. + if ( auto op = p.parent().tryAs(); op && op->kind() == operator_::Kind::Call ) + return; + + replaceNode(&p, expression::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta())); + } + + void operator()(const expression::ResolvedID& u, position_t p) { + auto& parent = p.parent(); + if ( auto op = parent.tryAs(); + op && op->operator_().kind() == operator_::Kind::Call ) + return; + + if ( auto op = parent.tryAs(); op && op->kind() == operator_::Kind::Call ) + // If we are inside a call expression, leave it alone. The operator + // resolver will take care of that. + return; + + // Look it up again because the AST may have changed the mapping. + // + // TODO(robin): Not quite sure in which cases this happen, ideally it + // shouldn't be necessary to re-lookup an ID once it has been + // resolved. + auto resolved = lookupID(u.id(), p); + + if ( ! resolved ) + return; + + // We replace the node, but don't flag the AST as modified because that + // could loop. + // + // Note: We *always* make the replacement even if nothing has changed + // because it's actually expensive to find out if the new node + // differs from the old. Originally, there was an if-statement (*) + // here, but it turns out that's super-expensive in terms of CPU + // performance, presumably because it needs to cycle through + // potentially large ASTs for the comparision. There was some + // evidence that it's expensive only in a debug build, but I didn't + // further investigate; just always doing the replacement seems to be + // the cheapest approach either way. + // + // (*) if ( (! u.isValid()) || u.declaration() != resolved->first->as() ) + replaceNode(&p, expression::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta()), false); + } + + void operator()(const declaration::Type& d, position_t p) { + auto type_id = ID(module_id, d.id()); + + std::optional cxx_id; + + if ( auto a = AttributeSet::find(d.attributes(), "&cxxname") ) + cxx_id = ID(*a->valueAs()); + + if ( d.type().typeID() != type_id ) { + auto nt = type::setTypeID(d.type(), std::move(type_id)); + + if ( cxx_id && d.cxxID() != *cxx_id ) + nt = type::setCxxID(nt, std::move(*cxx_id)); + + replaceNode(&p, declaration::Type::setType(d, nt)); + } + + else if ( cxx_id && d.cxxID() != *cxx_id ) { + auto nt = type::setCxxID(d.type(), std::move(*cxx_id)); + replaceNode(&p, declaration::Type::setType(d, nt)); + } + } +}; + +} // anonymous namespace + +bool hilti::detail::resolveIDs(Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/id-resolver"); + + auto v = Visitor(unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return v.modified; +} diff --git a/hilti/src/compiler/visitors/importer.cc b/hilti/src/compiler/visitors/importer.cc new file mode 100644 index 000000000..634889ef1 --- /dev/null +++ b/hilti/src/compiler/visitors/importer.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti; + +namespace { + +struct Visitor : public visitor::PreOrder { + Visitor(Unit* unit) : unit(unit) {} + std::set imported; + + void operator()(const declaration::ImportedModule& m) { + std::filesystem::path path; + + if ( m.path().empty() ) { + if ( auto x = unit->import(m.id(), m.extension(), m.scope(), m.searchDirectories()) ) + path = x->path; + else + logger().error(util::fmt("cannot import module '%s': %s", m.id(), x.error()), m); + } + else { + if ( auto x = unit->import(m.path()) ) { + if ( x->id != m.id() ) + logger().error(util::fmt("unexpected module '%s' in %s", x->id, path), m); + + path = m.path(); + } + else + logger().error(util::fmt("cannot import module %s: %s", m.path(), x.error()), m); + } + + imported.emplace(m.id(), path); + } + + Unit* unit; +}; + +} // anonymous namespace + + +std::set hilti::detail::importModules(const Node& root, Unit* unit) { + util::timing::Collector _("hilti/compiler/importer"); + + auto v = Visitor(unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return v.imported; +} diff --git a/hilti/src/compiler/visitors/operator-resolver.cc b/hilti/src/compiler/visitors/operator-resolver.cc new file mode 100644 index 000000000..95d009214 --- /dev/null +++ b/hilti/src/compiler/visitors/operator-resolver.cc @@ -0,0 +1,460 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using namespace hilti::detail; + +namespace hilti::logging::debug { +inline const DebugStream Resolver("resolver"); +} // namespace hilti::logging::debug + +/** Returns a set of overload alternatives matching given operand expression. */ +static std::vector _resolve(const std::vector& candidates, const std::vector& operands, + const Meta& meta, bool disallow_type_changes = false) { + static const std::vector> styles = { + CoercionStyle::PreferOriginalType | CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch, + CoercionStyle::PreferOriginalType | CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | + CoercionStyle::TryConstPromotion, + CoercionStyle::PreferOriginalType | CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | + CoercionStyle::TryConstPromotion | CoercionStyle::TryCoercion, + }; + + auto deref_operands = [&](const std::vector& ops) { + std::vector nops; + + for ( auto&& op : ops ) { + if ( type::isReferenceType(op.type()) ) + nops.push_back(builder::type_wrapped(builder::deref(op), op.type().dereferencedType())); + else + nops.push_back(op); + } + + return nops; + }; + + auto try_candidate = [&](const auto& c, const std::vector& ops, auto style, + const auto& dbg_msg) -> std::optional { + auto nops = coerceOperands(ops, c.operands(), style); + if ( ! nops ) { + if ( (style & CoercionStyle::TryCoercion) && ! (style & CoercionStyle::DisallowTypeChanges) ) { + // If any of the operands is a reference type, try the + // derefed operands, too. + for ( const auto& op : ops ) { + if ( type::isReferenceType(op.type()) ) + nops = coerceOperands(deref_operands(ops), c.operands(), style); + } + } + } + + if ( ! nops ) + return {}; + + auto r = c.instantiate(nops->second, meta); + HILTI_DEBUG(logging::debug::Resolver, util::fmt("-> %s, resolves to %s", dbg_msg, to_node(r))); + return r; + }; + + for ( auto style : styles ) { + if ( disallow_type_changes ) + style |= CoercionStyle::DisallowTypeChanges; + + HILTI_DEBUG(logging::debug::Resolver, util::fmt("style: %s", to_string(style))); + logging::DebugPushIndent _(logging::debug::Resolver); + + std::vector resolved; + + for ( const auto& c : candidates ) { + HILTI_DEBUG(logging::debug::Resolver, util::fmt("candidate: %s", c.typename_())); + logging::DebugPushIndent _(logging::debug::Resolver); + + if ( auto r = try_candidate(c, operands, style, "candidate matches") ) + resolved.emplace_back(std::move(*r)); + else { + // Try to swap the operators for commutative operators. + if ( operator_::is_commutative(c.kind()) && operands.size() == 2 ) { + if ( auto r = try_candidate(c, {operands[1], operands[0]}, style, + "candidate matches with operands swapped") ) + resolved.emplace_back(std::move(*r)); + } + } + } + + if ( resolved.size() ) + return resolved; + } + + return {}; +} + +namespace { + +/** Visitor that applies common AST transformation before the actual operator resolution process. */ +struct Normalizer : public hilti::visitor::PostOrder { + Normalizer(hilti::Module* module) : module(module) {} + + hilti::Module* module; + bool modified = false; + + template + void replaceNode(position_t* p, T&& n) { + auto x = p->node; + p->node = std::forward(n); + p->node.setOriginalNode(module->preserve(x)); + modified = true; + } + +#if 0 + void preDispatch(const Node& n, int level) override { + auto indent = std::string(level * 2, ' '); + std::cerr << "#1 " << indent << "> " << n.render() << std::endl; + n.scope()->render(std::cerr, " | "); + }; +#endif + + void operator()(const expression::UnresolvedOperator& u, position_t p) { + // Replace member operators that work on references with + // corresponding versions that first deref the target instance. + + auto deref_op0 = [&]() { + std::vector ops = u.operands(); + ops[0] = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Deref, {ops[0]}, ops[0].meta()); + Expression x = hilti::expression::UnresolvedOperator(u.kind(), std::move(ops), u.meta()); + replaceNode(&p, std::move(x)); + }; + + switch ( u.kind() ) { + case operator_::Kind::Member: + case operator_::Kind::MemberCall: + case operator_::Kind::HasMember: + case operator_::Kind::TryMember: { + if ( type::isReferenceType(u.operands()[0].type()) ) + deref_op0(); + } + default: { /* ignore */ + } + } + } +}; + +struct Visitor : public hilti::visitor::PostOrder { + bool modified = false; + +#if 0 + void preDispatch(const Node& n, int level) override { + auto indent = std::string(level * 2, ' '); + std::cerr << "#2 " << indent << "> " << n.render() << std::endl; + n.scope()->render(std::cerr, " | "); + }; +#endif + + bool resolveOperator(const expression::UnresolvedOperator& u, position_t p) { // TODO(google-runtime-references) + for ( const auto& o : u.operands() ) { + if ( o.type().isA() ) + return false; + } + + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("== resolving operator: %s (%s)", to_node(u), u.meta().location().render(true))); + logging::DebugPushIndent _(logging::debug::Resolver); + + std::vector resolved; + + // TODO(robin): This was meant to be "const auto&", but that crashes. Why? + auto candidates = operator_::registry().allOfKind(u.kind()); + + if ( u.kind() == operator_::Kind::MemberCall && u.operands().size() >= 2 ) { + // Pre-filter list of all member-call operators down to those + // with matching methods. This is just a performance + // optimization. + auto member = u.operands()[1].template as().id(); + + auto filtered = util::filter(candidates, [&](const auto& c) { + return std::get(c.operands()[1].type).template as() == member; + }); + + resolved = _resolve(candidates, u.operands(), u.meta()); + } + + else + resolved = _resolve(candidates, u.operands(), u.meta(), u.kind() == operator_::Kind::Cast); + + if ( resolved.empty() ) { + p.node.setError(util::fmt("cannot resolve operator: %s", renderOperatorInstance(u))); + return false; + } + + if ( resolved.size() > 1 ) { + p.node.setError(util::fmt("operator usage is ambigious: %s", renderOperatorInstance(u))); + p.node.augmentError("candidates:"); + for ( auto i : resolved ) + p.node.augmentError(util::fmt("- %s [%s]", + renderOperatorPrototype(i.as()), + i.typename_())); + + return true; + } + + p.node = resolved[0]; + modified = true; + +#ifndef NDEBUG + Expression new_op = p.node.as(); + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("=> resolved to %s (result: %s, expression is %s)", p.node.render(), new_op, + (new_op.isConstant() ? "const" : "non-const"))); +#endif + return true; + } + + bool resolveFunctionCall(const expression::UnresolvedOperator& u, position_t p) { + auto operands = u.operands(); + + if ( operands.size() != 2 ) + return false; + + auto callee = operands[0].tryAs(); + auto args_ctor = operands[1].tryAs(); + + if ( ! callee ) + return false; + + if ( ! args_ctor ) { + p.node.setError("function call's argument must be a tuple constant"); + return true; + } + + auto args = args_ctor->ctor().tryAs(); + + if ( ! args ) { + p.node.setError("function call's argument must be a tuple constant"); + return true; + } + + std::vector candidates; + + for ( auto i = p.path.rbegin(); i != p.path.rend(); i++ ) { + auto resolved = (**i).scope()->lookupAll(callee->id()); + + if ( resolved.empty() ) + continue; + + for ( auto& r : resolved ) { + auto d = r.node->tryAs(); + + if ( ! d ) { + p.node.setError(util::fmt("ID '%s' resolves to something other than just functions", callee->id())); + return true; + } + + if ( r.external && d->linkage() != declaration::Linkage::Public ) { + p.node.setError(util::fmt("function has not been declared public: %s", r.qualified)); + return true; + } + + auto op = operator_::function::Call::Operator(r, d->function().type()); + candidates.emplace_back(op); + } + + std::vector overloads = _resolve(candidates, operands, u.meta()); + + if ( overloads.empty() ) + break; + + if ( overloads.size() > 1 ) { + // Ok as long as it's all the same hook, report otherwise. + auto function = [](auto n) { + auto rid = + n.template as().op0().template as(); + return std::make_pair(rid.id(), rid.declaration().template as().function()); + }; + + auto [id, func] = function(overloads[0]); + + if ( func.type().flavor() != type::function::Flavor::Hook ) { + p.node.setError(util::fmt("call is ambigious: %s", renderOperatorInstance(u))); + p.node.augmentError("candidate functions:"); + for ( auto i : overloads ) + p.node.augmentError( + util::fmt("- %s", renderOperatorPrototype(i.as()))); + + return true; + } + + for ( auto& i : overloads ) { + auto [oid, ofunc] = function(i); + if ( id != oid || Type(func.type()) != Type(ofunc.type()) ) { + p.node.setError(util::fmt("call is ambigious: %s", renderOperatorInstance(u))); + p.node.augmentError("candidate functions:"); + for ( auto i : overloads ) + p.node.augmentError( + util::fmt("- %s", renderOperatorPrototype(i.as()))); + + return true; + } + } + } + + // Found a match. + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("resolved function call %s to %s", callee->id(), overloads.front().render()), + p.node.location()); + + p.node = overloads.front(); + modified = true; + return true; + } + + p.node.setError(util::fmt("call does not match any function: %s", renderOperatorInstance(u))); + + if ( ! candidates.empty() ) { + p.node.augmentError("candidate functions:"); + for ( const auto& i : candidates ) { + auto rop = i.instantiate(u.operands(), u.meta()).as(); + p.node.augmentError(util::fmt("- %s", renderOperatorPrototype(rop))); + } + } + + return true; + } + + bool resolveMethodCall(const expression::UnresolvedOperator& u, position_t p) { + auto operands = u.operands(); + + if ( operands.size() != 3 ) + return false; + + auto stype = type::effectiveType(operands[0].type()).tryAs(); + auto callee = operands[1].tryAs(); + auto args_ctor = operands[2].tryAs(); + + if ( ! (stype && callee) ) + return false; + + if ( ! args_ctor ) { + p.node.setError("method call's argument must be a tuple constant"); + return true; + } + + auto args = args_ctor->ctor().tryAs(); + + if ( ! args ) { + p.node.setError("method call's argument must be a tuple constant"); + return true; + } + + auto fields = stype->fields(callee->id()); + + if ( fields.empty() ) { + p.node.setError(util::fmt("struct type does not have a method `%s`", callee->id())); + return false; // Continue trying to find another match. + } + + for ( auto& f : fields ) { + if ( ! f.type().isA() ) { + p.node.setError(util::fmt("struct attribute '%s' is not a function", callee->id())); + return true; + } + } + + auto candidates = util::transform(fields, [&](const auto& f) -> Operator { + return operator_::struct_::MemberCall::Operator(*stype, f); + }); + + std::vector overloads = _resolve(candidates, operands, u.meta()); + + if ( overloads.empty() ) { + p.node.setError(util::fmt("call does not match any method: %s", renderOperatorInstance(u))); + + if ( ! candidates.empty() ) { + p.node.augmentError("candidate methods:"); + for ( const auto& i : candidates ) { + auto rop = i.instantiate(u.operands(), u.meta()).as(); + p.node.augmentError(util::fmt("- %s", renderOperatorPrototype(rop))); + } + } + + return true; + } + + if ( overloads.size() > 1 ) { + p.node.setError(util::fmt("method call to is ambigious: %s", renderOperatorInstance(u))); + p.node.augmentError("candidates:"); + for ( auto i : overloads ) + p.node.augmentError(util::fmt("- %s", renderOperatorPrototype(i.as()))); + + return true; + } + + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("resolved method call %s to %s", callee->id(), overloads.front().render()), + p.node.location()); + + p.node = overloads.front(); + modified = true; + return true; + } + + void operator()(const expression::UnresolvedOperator& u, position_t p) { + if ( u.kind() == operator_::Kind::Call && resolveFunctionCall(u, p) ) + return; + + if ( u.kind() == operator_::Kind::MemberCall && resolveMethodCall(u, p) ) + return; + + if ( resolveOperator(u, p) ) + return; + + if ( u.kind() == operator_::Kind::Cast ) { + // We hardcode here that a cast<> operator can always perform any + // legal coercion. This helps in cases where we need to force a + // specific coercion to take place. + auto expr = u.operands()[0]; + auto dst = u.operands()[1].as().typeValue(); + + if ( dst != type::unknown ) { + const auto style = CoercionStyle::TryAllForMatching | CoercionStyle::ContextualConversion; + if ( auto c = hilti::coerceExpression(expr, dst, style) ) { + HILTI_DEBUG(logging::debug::Resolver, util::fmt("resolved cast to type '%s' through coercion", dst), + p.node.location()); + + p.node = operator_::generic::CastedCoercion::Operator().instantiate(u.operands(), u.meta()); + modified = true; + return; + } + } + } + } +}; + +} // anonymous namespace + +bool detail::resolveOperators(Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/operator-resolver"); + + auto n = Normalizer(&root->as()); + for ( auto i : n.walk(root) ) + n.dispatch(i); + + auto v = Visitor(); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return n.modified || v.modified; +} diff --git a/hilti/src/compiler/visitors/printer.cc b/hilti/src/compiler/visitors/printer.cc new file mode 100644 index 000000000..e25d22705 --- /dev/null +++ b/hilti/src/compiler/visitors/printer.cc @@ -0,0 +1,1040 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +static std::string renderOperator(operator_::Kind kind, const std::vector& ops) { + switch ( kind ) { + case operator_::Kind::Add: return fmt("add %s[%s]", ops[0], ops[1]); + case operator_::Kind::Begin: return fmt("begin(%s)", ops[0]); + case operator_::Kind::BitAnd: return fmt("%s & %s", ops[0], ops[1]); + case operator_::Kind::BitOr: return fmt("%s | %s", ops[0], ops[1]); + case operator_::Kind::BitXor: return fmt("%s ^ %s", ops[0], ops[1]); + case operator_::Kind::Call: return fmt("%s%s", ops[0], ops[1]); + case operator_::Kind::Cast: return fmt("cast<%s>(%s)", ops[1], ops[0]); + case operator_::Kind::DecrPostfix: return fmt("%s--", ops[0]); + case operator_::Kind::DecrPrefix: return fmt("--%s", ops[0]); + case operator_::Kind::Delete: return fmt("delete %s[%s]", ops[0], ops[1]); + case operator_::Kind::Deref: return fmt("(*%s)", ops[0]); + case operator_::Kind::Difference: return fmt("%s - %s", ops[0], ops[1]); + case operator_::Kind::DifferenceAssign: return fmt("%s -= %s", ops[0], ops[1]); + case operator_::Kind::Division: return fmt("%s / %s", ops[0], ops[1]); + case operator_::Kind::DivisionAssign: return fmt("%s /= %s", ops[0], ops[1]); + case operator_::Kind::Equal: return fmt("%s == %s", ops[0], ops[1]); + case operator_::Kind::End: return fmt("end(%s)", ops[0]); + case operator_::Kind::Greater: return fmt("%s > %s", ops[0], ops[1]); + case operator_::Kind::GreaterEqual: return fmt("%s >= %s", ops[0], ops[1]); + case operator_::Kind::HasMember: return fmt("%s?.%s", ops[0], ops[1]); + case operator_::Kind::In: return fmt("%s in %s", ops[0], ops[1]); + case operator_::Kind::IncrPostfix: return fmt("%s++", ops[0]); + case operator_::Kind::IncrPrefix: return fmt("++%s", ops[0]); + case operator_::Kind::Index: return fmt("%s[%s]", ops[0], ops[1]); + case operator_::Kind::Lower: return fmt("%s < %s", ops[0], ops[1]); + case operator_::Kind::LowerEqual: return fmt("%s <= %s", ops[0], ops[1]); + case operator_::Kind::Member: return fmt("%s.%s", ops[0], ops[1]); + case operator_::Kind::MemberCall: return fmt("%s.%s%s", ops[0], ops[1], ops[2]); + case operator_::Kind::Modulo: return fmt("%s %% %s", ops[0], ops[1]); + case operator_::Kind::Multiple: return fmt("%s * %s", ops[0], ops[1]); + case operator_::Kind::MultipleAssign: return fmt("%s *= %s", ops[0], ops[1]); + case operator_::Kind::Negate: return fmt("~%s", ops[0]); + case operator_::Kind::New: return fmt("new %s%s", ops[0], ops[1]); + case operator_::Kind::Power: return fmt("%s ** %s", ops[0], ops[1]); + case operator_::Kind::ShiftLeft: return fmt("%s << %s", ops[0], ops[1]); + case operator_::Kind::ShiftRight: return fmt("%s >> %s", ops[0], ops[1]); + case operator_::Kind::SignNeg: return fmt("-%s", ops[0]); + case operator_::Kind::SignPos: return fmt("+%s", ops[0]); + case operator_::Kind::Size: return fmt("|%s|", ops[0]); + case operator_::Kind::Sum: return fmt("%s + %s", ops[0], ops[1]); + case operator_::Kind::SumAssign: return fmt("%s += %s", ops[0], ops[1]); + case operator_::Kind::TryMember: return fmt("%s.?%s", ops[0], ops[1]); + case operator_::Kind::Unequal: return fmt("%s != %s", ops[0], ops[1]); + case operator_::Kind::Unpack: return fmt("unpack<%s>(%s)", ops[0], ops[1]); + + case operator_::Kind::Unknown: logger().internalError("\"unknown\" operator"); + default: util::cannot_be_reached(); + } +} + +static std::string renderExpressionType(const Expression& e) { + auto const_ = (e.isConstant() && type::isMutable(e.type()) ? "const " : ""); + return fmt("%s%s", const_, e.type()); +} + +static std::string renderOperand(operator_::Operand op, const std::vector& exprs) { + auto t = operator_::type(op.type, exprs, exprs); + std::string s = (t ? fmt("%s", *t) : ""); + + if ( op.default_ ) + s = fmt("%s=%s", s, *op.default_); + + if ( op.optional || op.default_ ) + s = fmt("[%s]", s); + + return s; +} + +namespace { + +struct Visitor : visitor::PreOrder { + Visitor(printer::Stream& out) : out(out) {} // NOLINT + + void printFunctionType(const type::Function& ftype, const std::optional& id) { + if ( ftype.isWildcard() ) { + out << ""; + return; + } + + if ( ftype.flavor() != type::function::Flavor::Standard ) + out << to_string(ftype.flavor()) << ' '; + + out << ftype.result() << ' '; + + if ( id ) + out << *id; + + out << '('; + out << std::make_pair(ftype.parameters(), ", "); + out << ')'; + } + + auto linkage(declaration::Linkage l) { + switch ( l ) { + case declaration::Linkage::Init: return "init "; + case declaration::Linkage::Struct: return "method "; + case declaration::Linkage::Private: return ""; // That's the default. + case declaration::Linkage::Public: return "public "; + default: util::cannot_be_reached(); + } + } + + auto const_(const Type& t) { return (out.isCompact() && type::isConstant(t)) ? "const " : ""; } + + void operator()(const Attribute& n) { + out << n.tag(); + + if ( n.hasValue() ) + out << "=" << n.value(); + } + + void operator()(const AttributeSet& n) { + bool first = true; + for ( const auto& a : n.attributes() ) { + if ( ! first ) + out << ' '; + else + first = false; + + out << a; + } + } + + void operator()(const type::function::Result& n) { out << n.type(); } + + void operator()(const Function& n) { + if ( n.callingConvention() != function::CallingConvention::Standard ) + out << to_string(n.callingConvention()) << ' '; + + printFunctionType(n.type(), n.id()); + + if ( n.attributes() ) + out << ' ' << std::make_pair(n.attributes()->attributes(), " "); + + if ( n.body() ) + out << ' ' << *n.body(); + else + out << ';' << out.newline(); + } + + void operator()(const ID& n) { + if ( n.namespace_() == out.currentScope() ) + out << std::string(n.local()); + else + out << std::string(n); + } + + void operator()(const Module& n) { + out.beginLine(); + out << "module " << n.id() << " {" << out.newline(); + out.endLine(); + + out.pushScope(n.id()); + + auto printDecls = [&](auto decls) { + for ( const auto& d : decls ) + out << d; + + if ( decls.size() ) + out.emptyLine(); + }; + + printDecls( + util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + printDecls(util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + printDecls(util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + printDecls( + util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + printDecls(util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + + for ( const auto& s : n.statements().statements() ) + out << s; + + if ( ! n.statements().statements().empty() ) + out.emptyLine(); + + out.popScope(); + + out.beginLine(); + out << "}"; + out.endLine(); + } + + ////// Ctors + + void operator()(const ctor::Address& n) { out << n.value(); } + + void operator()(const ctor::Bool& n) { out << (n.value() ? "True" : "False"); } + + void operator()(const ctor::Bytes& n) { out << "b\"" << util::escapeUTF8(n.value(), true) << '"'; } + + void operator()(const ctor::Coerced& n) { out << n.originalCtor(); } + + void operator()(const ctor::Default& n) { + out << "default<" << n.type() << ">(" << std::make_pair(n.typeArguments(), ", ") << ")"; + } + + void operator()(const ctor::Enum& n) { out << *n.type().typeID() << "::" << n.value(); } + + void operator()(const ctor::Error& n) { out << "error(\"" << n.value() << "\")"; } + + void operator()(const ctor::Interval& n) { out << n.value(); } + + void operator()(const ctor::List& n) { out << '[' << std::make_pair(n.value(), ", ") << ']'; } + + void operator()(const ctor::Map& n) { + auto elems = util::transform(n.value(), [](const auto& e) { return fmt("%s: %s", e.first, e.second); }); + out << "map(" << std::make_pair(elems, ", ") << ')'; + } + + void operator()(const ctor::Network& n) { out << n.value(); } + + void operator()(const ctor::Null& n) { out << "Null"; } + + void operator()(const ctor::Optional& n) { + if ( n.value() ) + out << *n.value(); + else + out << "Null"; + } + + void operator()(const ctor::Port& n) { out << n.value(); } + + void operator()(const ctor::Real& n) { out << fmt("%a", n.value()); } + + void operator()(const ctor::StrongReference& n) { out << "Null"; } + + void operator()(const ctor::RegExp& n) { + out << std::make_pair(util::transform(n.value(), [](auto p) { return fmt("/%s/", p); }), " |"); + } + + void operator()(const ctor::Result& n) { + if ( n.value() ) + out << *n.value(); + else + out << *n.error(); + } + + void operator()(const ctor::Set& n) { out << "set(" << std::make_pair(n.value(), ", ") << ')'; } + + void operator()(const ctor::SignedInteger& n) { out << n.value(); } + + void operator()(const ctor::Stream& n) { out << "stream(" << util::escapeUTF8(n.value(), true) << ')'; } + + void operator()(const ctor::String& n) { out << '"' << util::escapeUTF8(n.value(), true) << '"'; } + + void operator()(const ctor::Struct& n) { + out << "["; + + bool first = true; + for ( const auto& f : n.fields() ) { + if ( ! first ) + out << ", "; + else + first = false; + + out << '$' << f.first << "=" << f.second; + } + + out << "]"; + } + + void operator()(const ctor::Time& n) { out << n.value(); } + + void operator()(const ctor::Tuple& n) { out << '(' << std::make_pair(n.value(), ", ") << ')'; } + + void operator()(const ctor::UnsignedInteger& n) { out << n.value(); } + + void operator()(const ctor::Vector& n) { out << "vector(" << std::make_pair(n.value(), ", ") << ')'; } + + void operator()(const ctor::WeakReference& n) { out << "Null"; } + + void operator()(const ctor::ValueReference& n) { out << "value_ref(" << n.expression() << ')'; } + + ////// Declarations + + void operator()(const declaration::Constant& n) { + out.beginLine(); + out << linkage(n.linkage()) << "const " << n.id() << " = " << n.value() << ';'; + out.endLine(); + } + + void operator()(const declaration::Expression& n) { out << n.expression(); } + + void operator()(const declaration::Parameter& n) { + auto kind = [&](auto k) { + switch ( k ) { + case declaration::parameter::Kind::Copy: return "copy "; + case declaration::parameter::Kind::In: return ""; + case declaration::parameter::Kind::InOut: return "inout "; + case declaration::parameter::Kind::Unknown: logger().internalError("parameter kind not set"); + default: util::cannot_be_reached(); + } + }; + + out << kind(n.kind()) << n.type() << ' ' << n.id(); + + if ( n.default_() ) + out << " = " << *n.default_(); + } + + void operator()(const declaration::Function& n) { + out.beginLine(); + + const auto& func = n.function(); + + if ( ! func.body() ) + out << "declare "; + else + out.emptyLine(); + + out << linkage(n.linkage()); + + if ( n.linkage() != declaration::Linkage::Struct ) + out << "function "; + + out << n.function(); + } + + void operator()(const declaration::ImportedModule& n) { + out.beginLine(); + out << "import " << n.id() << ';'; + out.endLine(); + } + + void operator()(const declaration::Type& n) { + out.beginLine(); + out << linkage(n.linkage()) << "type " << n.id() << " = "; + out.setExpandSubsequentType(true); + out << n.type(); + + if ( n.attributes() ) + out << ' ' << *n.attributes(); + + out << ';'; + out.endLine(); + } + + void operator()(const declaration::LocalVariable& n) { + // Will be printed through a statement, hence no outer formatting. + out << "local "; + + if ( n.hasAutomaticType() ) + out << "auto"; + else + out << n.type(); + + out << ' ' << n.id(); + + if ( n.typeArguments().size() ) + out << '(' << std::make_pair(n.typeArguments(), ", ") << ')'; + + if ( n.init() ) + out << " = " << *n.init(); + } + + void operator()(const declaration::GlobalVariable& n) { + out.beginLine(); + out << linkage(n.linkage()) << "global "; + + if ( n.hasAutomaticType() ) + out << "auto"; + else + out << n.type(); + + out << ' ' << n.id(); + + if ( n.typeArguments().size() ) + out << '(' << std::make_pair(n.typeArguments(), ", ") << ')'; + + if ( n.init() ) + out << " = " << *n.init(); + + out << ';'; + out.endLine(); + } + + ////// Expressions + + void operator()(const expression::Assign& n) { out << n.target() << " = " << n.source(); } + + void operator()(const expression::Coerced& n) { out << n.expression(); } + + void operator()(const expression::Ctor& n) { out << n.ctor(); } + + result_t operator()(const expression::Keyword& n) { + switch ( n.kind() ) { + case expression::keyword::Kind::Self: out << "self"; + case expression::keyword::Kind::DollarDollar: out << "$$"; + } + } + + result_t operator()(const expression::ListComprehension& n) { + out << '[' << n.output() << " for " << n.id() << " in " << n.input(); + + if ( n.condition() ) + out << " if " << *n.condition(); + + out << ']'; + } + + result_t operator()(const expression::LogicalAnd& n) { out << n.op0() << " && " << n.op1(); } + + result_t operator()(const expression::LogicalNot& n) { out << "! " << n.expression(); } + + result_t operator()(const expression::LogicalOr& n) { out << n.op0() << " || " << n.op1(); } + + result_t operator()(const expression::Member& n) { out << n.id(); } + + result_t operator()(const expression::Move& n) { out << "move(" << n.expression() << ")"; } + + void operator()(const expression::ResolvedID& n) { out << n.id(); } + + result_t operator()(const expression::Ternary& n) { + out << n.condition() << " ? " << n.true_() << " : " << n.false_(); + } + + result_t operator()(const expression::Type_& n) { + if ( auto id = n.typeValue().typeID() ) + out << *id; + else + out << n.typeValue(); + } + + result_t operator()(const expression::TypeWrapped& n) { out << n.expression(); } + + void operator()(const expression::UnresolvedID& n) { out << n.id(); } + + void operator()(const expression::Void& n) { + out << ""; // Shouldn't really happen. + } + + ////// Statements + + void operator()(const statement::Assert& n) { + out.beginLine(); + + if ( n.expectsException() ) + out << "assert-exception "; + else + out << "assert "; + + out << n.expression(); + if ( n.message() ) + out << " : " << *n.message(); + out << ";"; + out.endLine(); + } + + void operator()(const statement::Block& n) { + if ( out.indent() == 0 || n.statements().size() > 1 ) + out << "{"; + + out.endLine(); + out.incrementIndent(); + + const auto& stmts = n.statements(); + for ( auto [i, s] : util::enumerate(stmts) ) { + out.setPositionInBlock(i == 0, i == (stmts.size() - 1)); + + if ( s.isA() ) + out.beginLine(); + + out << s; + + if ( s.isA() ) + out.endLine(); + } + + out.decrementIndent(); + + if ( out.indent() == 0 || n.statements().size() > 1 ) { + out.beginLine(); + out << "}"; + out.endLine(); + } + } + + void operator()(const statement::Break& n) { + out.beginLine(); + out << "break;"; + out.endLine(); + } + + void operator()(const statement::Continue& n) { + out.beginLine(); + out << "continue;"; + out.endLine(); + } + + void operator()(const statement::Comment& n) { + if ( (n.separator() == hilti::statement::comment::Separator::Before || + n.separator() == hilti::statement::comment::Separator::BeforeAndAfter) && + ! out.isFirstInBlock() ) + out.emptyLine(); + + out.beginLine(); + out << "# " << n.comment(); + out.endLine(); + + if ( (n.separator() == hilti::statement::comment::Separator::After || + n.separator() == hilti::statement::comment::Separator::BeforeAndAfter) && + ! out.isLastInBlock() ) + out.emptyLine(); + } + + void operator()(const statement::Declaration& n) { + out.beginLine(); + out << n.declaration() << ';'; + out.endLine(); + } + + void operator()(const statement::Expression& n) { + out.beginLine(); + out << n.expression() << ';'; + out.endLine(); + } + + void operator()(const statement::For& n) { + out.emptyLine(); + out.beginLine(); + out << "for ( " << n.id() << " in " << n.sequence() << " ) " << n.body(); + out.endLine(); + } + + void operator()(const statement::If& n) { + out.emptyLine(); + out.beginLine(); + out << "if ( "; + + if ( auto e = n.init() ) + out << *e << "; "; + + if ( auto e = n.condition() ) + out << *e; + + out << " ) " << n.true_(); + + if ( n.false_() ) { + out.beginLine(); + out << "else " << *n.false_(); + } + + out.endLine(); + } + + void operator()(const statement::Return& n) { + out.beginLine(); + out << "return"; + + if ( auto e = n.expression() ) + out << ' ' << *e; + + out << ';'; + out.endLine(); + } + + void operator()(const statement::Switch& n) { + out.emptyLine(); + out.beginLine(); + out << "switch ( "; + + if ( auto i = n.init() ) + out << *i; + else + out << n.expression(); + + out << " ) {"; + out.incrementIndent(); + out.endLine(); + + for ( const auto& c : n.cases() ) { + out.beginLine(); + out << "case " << std::make_pair(c.expressions(), ", ") << ": " << c.body(); + out.endLine(); + } + + if ( auto c = n.default_() ) { + out.beginLine(); + out << "default: " << c->body(); + out.endLine(); + } + + out.decrementIndent(); + out.beginLine(); + out << "}"; + out.endLine(); + } + + void operator()(const statement::Throw& n) { + out.beginLine(); + out << "throw"; + + if ( auto e = n.expression() ) + out << fmt(" %s", *e); + + out << ";"; + out.endLine(); + } + + void operator()(const statement::try_::Catch& n) { + out << "catch "; + + if ( auto p = n.parameter() ) + out << "( " << Declaration(*p) << " ) "; + + out << n.body(); + } + + void operator()(const statement::Try& n) { + out.beginLine(); + out << "try " << n.body(); + + for ( const auto& c : n.catches() ) + out << c; + + out.endLine(); + } + + void operator()(const statement::While& n) { + out.emptyLine(); + out.beginLine(); + out << "while ( "; + + if ( auto e = n.init() ) + out << *e << "; "; + + if ( auto e = n.condition() ) + out << *e; + + out << " ) " << n.body(); + + if ( n.else_() ) { + out.beginLine(); + out << "else " << *n.else_(); + } + + out.endLine(); + } + + void operator()(const statement::Yield& n) { + out.beginLine(); + out << "yield"; + out.endLine(); + } + + void operator()(const expression::ResolvedOperator& n) { + out << renderOperator(n.operator_().kind(), util::transform(n.operands(), [](auto o) { return fmt("%s", o); })); + } + + void operator()(const expression::UnresolvedOperator& n) { + out << renderOperator(n.kind(), util::transform(n.operands(), [](auto o) { return fmt("%s", o); })); + } + + ////// Types + + void operator()(const type::Any& n) { out << const_(n) << "any"; } + + void operator()(const type::Address& n) { out << const_(n) << "addr"; } + + void operator()(const type::Bool& n) { out << const_(n) << "bool"; } + + void operator()(const type::Bytes& n) { out << const_(n) << "bytes"; } + + void operator()(const type::Computed& n) { out << const_(n) << n.type(); } + + void operator()(const type::enum_::Label& n) { out << n.id() << " = " << n.value(); } + + void operator()(const type::Enum& n) { + if ( ! out.isExpandSubsequentType() ) { + out.setExpandSubsequentType(false); + if ( auto id = n.typeID() ) { + out << *id; + return; + } + } + + out.setExpandSubsequentType(false); + + auto x = util::filter(n.labels(), [](auto l) { return l.id() != ID("Undef"); }); + out << const_(n) << "enum { " << std::make_pair(std::move(x), ", ") << " }"; + } + + void operator()(const type::Error& n) { out << const_(n) << "error"; } + + void operator()(const type::Exception& n) { + out << const_(n) << "exception"; + + if ( auto t = n.baseType() ) { + out << " : "; + if ( auto id = t->typeID() ) + out << *id; + else + out << *t; + } + } + + void operator()(const type::Function& n) { + out << const_(n) << "function "; + printFunctionType(n, {}); + } + + void operator()(const type::Interval& n) { out << const_(n) << "interval"; } + + void operator()(const type::Member& n) { out << const_(n) << ""; } + + void operator()(const type::Network& n) { out << const_(n) << "net"; } + + void operator()(const type::Null& n) { out << const_(n) << ""; } + + void operator()(const type::OperandList& n) { out << const_(n) << ""; } + + void operator()(const type::Optional& n) { + if ( n.isWildcard() ) + out << const_(n) << "optional<*>"; + else { + out << const_(n) << "optional<" << n.dereferencedType() << ">"; + } + } + + void operator()(const type::Port& n) { out << const_(n) << "port"; } + + void operator()(const type::Real& n) { out << const_(n) << "real"; } + + void operator()(const type::StrongReference& n) { + if ( n.isWildcard() ) + out << const_(n) << "strong_ref<*>"; + else + out << const_(n) << "strong_ref<" << n.dereferencedType() << ">"; + } + + void operator()(const type::Stream& n) { out << const_(n) << "stream"; } + + void operator()(const type::bytes::Iterator& n) { out << const_(n) << "iterator"; } + + void operator()(const type::list::Iterator& n) { + if ( n.isWildcard() ) + out << const_(n) << "iterator>"; + else + out << const_(n) << fmt("iterator>", n.dereferencedType()); + } + + void operator()(const type::stream::Iterator& n) { out << const_(n) << "iterator"; } + + void operator()(const type::vector::Iterator& n) { + if ( n.isWildcard() ) + out << const_(n) << "iterator>"; + else + out << const_(n) << fmt("iterator>", n.dereferencedType()); + } + + void operator()(const type::stream::View& n) { out << const_(n) << "view"; } + + void operator()(const type::Library& n) { + if ( auto id = n.typeID() ) + out << const_(n) << *id; + else + out << const_(n) << fmt("__library_type(\"%s\")", n.cxxName()); + } + + void operator()(const type::List& n) { + if ( n.isWildcard() ) + out << const_(n) << "list<*>"; + else { + out << const_(n) << "list<" << n.elementType() << ">"; + } + } + + void operator()(const type::Map& n) { + if ( n.isWildcard() ) + out << const_(n) << "map<*>"; + else { + out << const_(n) << "map<" << n.keyType() << ", " << n.elementType() << ">"; + } + } + + void operator()(const type::RegExp& n) { out << const_(n) << "regexp"; } + + void operator()(const type::ResolvedID& n) { out << const_(n) << n.id(); } + + void operator()(const type::Result& n) { + if ( n.isWildcard() ) + out << const_(n) << "result<*>"; + else { + out << const_(n) << "result<" << n.dereferencedType() << ">"; + } + } + + void operator()(const type::Set& n) { + if ( n.isWildcard() ) + out << const_(n) << "set<*>"; + else { + out << const_(n) << "set<" << n.elementType() << ">"; + } + } + + void operator()(const type::SignedInteger& n) { + if ( n.isWildcard() ) + out << const_(n) << "int<*>"; + else + out << const_(n) << fmt("int<%d>", n.width()); + } + + void operator()(const type::String& n) { out << const_(n) << "string"; } + + void operator()(const type::struct_::Field& n) { + out << " "; + + if ( auto ft = n.type().tryAs() ) { + out << to_string(ft->flavor()) << " "; + + if ( n.callingConvention() != function::CallingConvention::Standard ) + out << to_string(n.callingConvention()) << ' '; + + out << ft->result().type() << " " << n.id() << "(" << std::make_pair(ft->parameters(), ", ") << ")"; + } + + else + out << n.type() << ' ' << n.id(); + + if ( n.attributes() ) + out << ' ' << *n.attributes(); + + out << ";" << out.newline(); + } + + void operator()(const type::Struct& n, position_t p) { + if ( ! out.isExpandSubsequentType() ) { + if ( auto id = n.typeID() ) { + out << *id; + + if ( n.parameters().size() ) + out << '(' << std::make_pair(n.parameters(), ", ") << ')'; + + return; + } + } + + out.setExpandSubsequentType(false); + + out << const_(n) << "struct"; + + if ( n.parameters().size() ) + out << " (" << std::make_pair(n.parameters(), ", ") << ')'; + + out << " {" << out.newline(); + + for ( const auto& f : n.fields() ) { + if ( ! f.type().isA() ) + out << f; + } + + for ( const auto& f : n.fields() ) { + if ( f.type().isA() ) + out << f; + } + + out << "}"; + } + + void operator()(const type::Time& n) { out << const_(n) << "time"; } + + void operator()(const type::Type_& n) { out << const_(n) << fmt("type<%s>", n.typeValue()); } + + void operator()(const type::union_::Field& n) { + out << " " << n.type() << ' ' << n.id(); + + if ( n.attributes() ) + out << ' ' << *n.attributes(); + + out << ";" << out.newline(); + } + + void operator()(const type::Union& n, position_t p) { + if ( ! out.isExpandSubsequentType() ) { + if ( auto id = n.typeID() ) { + out << *id; + return; + } + } + + out.setExpandSubsequentType(false); + + out << const_(n) << "union {" << out.newline(); + + for ( const auto& f : n.fields() ) + out << f; + + out << "}"; + } + + void operator()(const type::Unknown& n) { out << const_(n) << ""; } + + void operator()(const type::UnsignedInteger& n) { + if ( n.isWildcard() ) + out << const_(n) << "uint<*>"; + else + out << const_(n) << fmt("uint<%d>", n.width()); + } + + void operator()(const type::Tuple& n) { + if ( n.isWildcard() ) + out << const_(n) << "tuple<*>"; + else { + out << const_(n) << "tuple<" << std::make_pair(n.types(), ", ") << ">"; + } + } + + void operator()(const type::UnresolvedID& n) { out << const_(n) << n.id(); } + + void operator()(const type::Vector& n) { + if ( n.isWildcard() ) + out << const_(n) << "vector<*>"; + else { + out << const_(n) << "vector<" << n.elementType() << ">"; + } + } + + void operator()(const type::Void& n) { out << const_(n) << "void"; } + + void operator()(const type::WeakReference& n) { + if ( n.isWildcard() ) + out << const_(n) << "weak_ref<*>"; + else + out << const_(n) << "weak_ref<" << n.dereferencedType() << ">"; + } + + void operator()(const type::ValueReference& n) { + if ( n.isWildcard() ) + out << const_(n) << "value_ref<*>"; + else + out << const_(n) << "value_ref<" << n.dereferencedType() << ">"; + } + +private: + printer::Stream& out; +}; + +} // anonymous namespace + +void hilti::detail::printAST(const Node& root, std::ostream& out, bool compact) { + auto stream = printer::Stream(out, compact); + printAST(root, stream); +} + +void hilti::detail::printAST(const Node& root, printer::Stream& stream) { + util::timing::Collector _("hilti/printer"); + + for ( auto& p : plugin::registry().plugins() ) { + if ( ! p.print_ast ) + continue; + + if ( (*p.print_ast)(root, stream) ) + return; + } + + Visitor(stream).dispatch(root); +} + +std::string hilti::detail::renderOperatorPrototype(const expression::ResolvedOperator& o) { + const auto& op = o.operator_(); + const auto& exprs = o.operands(); + + switch ( op.kind() ) { + case operator_::Kind::Call: { + assert(exprs.size() == 2); + auto id = exprs[0]; + auto ops = + operator_::type(o.operator_().operands()[1].type, exprs, exprs)->as().operands(); + auto args = + util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderOperand(x, exprs)); }), ", "); + return fmt("%s(%s)", id, args); + } + + case operator_::Kind::MemberCall: { + assert(exprs.size() == 3); + auto self = exprs[0]; + auto id = exprs[1]; + auto ops = + operator_::type(o.operator_().operands()[2].type, exprs, exprs)->as().operands(); + auto args = + util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderOperand(x, exprs)); }), ", "); + return fmt("<%s>.%s(%s)", renderExpressionType(self), id, args); + } + + default: + return renderOperator(op.kind(), util::transform(op.operands(), [&](auto x) { + return fmt("<%s>", renderOperand(x, exprs)); + })); + } +} + +static std::string _renderOperatorInstance(operator_::Kind kind, const std::vector& exprs) { + switch ( kind ) { + case operator_::Kind::Call: { + assert(exprs.size() == 2); + auto id = exprs[0]; + auto ops = exprs[1].as().ctor().as().value(); + auto args = + util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); + return fmt("%s(%s)", id, args); + } + + case operator_::Kind::MemberCall: { + assert(exprs.size() == 3); + auto self = exprs[0]; + auto id = exprs[1]; + auto ops = exprs[2].as().ctor().as().value(); + auto args = + util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); + return fmt("<%s>.%s(%s)", renderExpressionType(self), id, args); + } + + default: + return renderOperator(kind, + util::transform(exprs, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); })); + } +} + +std::string hilti::detail::renderOperatorInstance(const expression::ResolvedOperator& o) { + return _renderOperatorInstance(o.operator_().kind(), o.operands()); +} + +std::string hilti::detail::renderOperatorInstance(const expression::UnresolvedOperator& o) { + return _renderOperatorInstance(o.kind(), o.operands()); +} diff --git a/hilti/src/compiler/visitors/renderer.cc b/hilti/src/compiler/visitors/renderer.cc new file mode 100644 index 000000000..1e8968895 --- /dev/null +++ b/hilti/src/compiler/visitors/renderer.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +static void render(const Node& n, std::ostream* out, std::optional dbg, bool include_scopes) { + util::timing::Collector _("hilti/renderer"); + + int dbg_level = 0; + + for ( const auto& i : visitor::PreOrder<>().walk(n) ) { + int new_dbg_level = i.path.size(); + + while ( dbg_level < new_dbg_level ) { + logger().debugPushIndent(*dbg); + dbg_level++; + } + + while ( dbg_level > new_dbg_level ) { + logger().debugPopIndent(*dbg); + dbg_level--; + } + +#if 0 + // Condense AST output, struct types can be very long. + if ( i.findParent() ) + continue; +#endif + + if ( out ) + (*out) << std::string(dbg_level - 1, ' '); + + auto s = fmt("- %s", i.node.render()); + + if ( out ) + (*out) << s << '\n'; + + if ( dbg ) + HILTI_DEBUG(*dbg, s); + + if ( include_scopes ) { + std::stringstream buffer; + i.node.scope()->render(buffer, " | "); + + if ( buffer.str().size() ) { + if ( out ) + (*out) << buffer.str(); + + if ( dbg ) { + for ( const auto& line : util::split(buffer.str(), "\n") ) { + if ( line.size() ) + HILTI_DEBUG(*dbg, line); + } + } + } + } + } + + while ( dbg_level-- > 0 ) + logger().debugPopIndent(*dbg); +} + +void detail::renderNode(const Node& n, std::ostream& out, bool include_scopes) { + ::render(n, &out, {}, include_scopes); +} + +void detail::renderNode(const Node& n, logging::DebugStream stream, bool include_scopes) { + ::render(n, nullptr, stream, include_scopes); +} diff --git a/hilti/src/compiler/visitors/scope-builder.cc b/hilti/src/compiler/visitors/scope-builder.cc new file mode 100644 index 000000000..e89702ae1 --- /dev/null +++ b/hilti/src/compiler/visitors/scope-builder.cc @@ -0,0 +1,306 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; + +namespace { + +struct VisitorPass1 : public visitor::PostOrder { + explicit VisitorPass1(Unit* unit) : unit(unit) {} + Unit* unit; + + void operator()(const Module& m, position_t p) { p.node.scope()->insert(m.id(), NodeRef(p.node)); } + + void operator()(const declaration::ImportedModule& m, position_t p) { + auto& other = unit->imported(m.id()); + p.node = declaration::ImportedModule::setModule(m, NodeRef(other)); + p.node.setScope(other.scope()); + } + + void operator()(const type::Function& m, position_t p) { + if ( p.parent().isA() ) + p.node.scope()->moveInto(p.parent().scope().get()); + } + + void operator()(const type::Enum& m, position_t p) { + if ( auto t = p.parent().tryAs() ) { + for ( const auto& l : m.labels() ) { + auto e = expression::Ctor(ctor::Enum(l, NodeRef(p.parent()), l.meta()), l.meta()); + auto d = declaration::Constant(l.id(), std::move(e), t->linkage(), l.meta()); + p.parent().scope()->insert(l.id(), Declaration(std::move(d))); + } + } + } + + void operator()(const type::Struct& m, position_t p) { + if ( auto t = p.parent().tryAs() ) { + auto id = ID("self", m.meta()); + auto type = type::Computed(NodeRef(p.parent()), + [](Node& n) { return type::ValueReference(n.as().type()); }); + auto self = expression::Keyword(expression::keyword::Kind::Self, type, m.meta()); + auto d = declaration::Expression(id, self, declaration::Linkage::Private, m.meta()); + p.parent().scope()->insert(id, Declaration(d)); + + // Make parameters accessible + for ( auto&& x : p.node.as().parameterNodes() ) + p.parent().scope()->insert(x->as().id(), NodeRef(x)); + + for ( auto& f : m.fields() ) { + // If &id is specified, make field directly accessible under + // given ID (i.e., as alias to "self.[...]"). + ID id; + + if ( auto x = AttributeSet::find(f.attributes(), "&id") ) + id = ID(*x->valueAs(), f.meta()); + + if ( id ) { + Expression self = + expression::ResolvedID("self", NodeRef(p.parent().scope()->lookup("self")->node), f.meta()); + + self = Expression( + operator_::value_reference::Deref::Operator().instantiate({std::move(self)}, f.meta())); + + auto e = + operator_::struct_::MemberConst::Operator().instantiate({std::move(self), + expression::Member(f.id(), f.meta())}, + f.meta()); + + auto d = declaration::Expression(id, std::move(e), {}, declaration::Linkage::Private, f.meta()); + + p.parent().scope()->insert(id, Declaration(d)); + } + + if ( f.isStatic() ) { + // Insert static member into struct's namespace. + auto field_id = f.id(); + auto module_id = p.template findParent()->get().id(); + auto qualified_id = ID(module_id, t->id(), f.id()); + + std::optional decl; + + if ( f.type().isA() ) { + auto wrapper = type::Computed(NodeRef(p.node), [field_id](auto n) { + auto t = n.template as(); + return t.field(field_id)->type(); + }); + + auto nf = Function(f.id(), wrapper, {}, f.callingConvention()); + decl = declaration::Function(std::move(nf), t->linkage(), m.meta()); + } + else + // Using a local here is cheating a bit: We just need to + // get the ID through to codegen. + decl = declaration::LocalVariable(qualified_id, f.type()); + + p.parent().scope()->insert(f.id(), *decl); + } + } + } + } + + void operator()(const statement::Switch& s, position_t p) { + auto wrapper = + type::Computed(NodeRef(p.node), [](auto n) { return n.template as().type(); }); + + auto d = declaration::LocalVariable(ID("__x"), wrapper, {}, true, s.meta()); + p.node.scope()->insert(d.id(), Declaration(d)); + } + + void operator()(const statement::Declaration& d, position_t p) { + p.node.scope()->moveInto(p.parent().scope().get()); + } + + void operator()(const declaration::Parameter& d, position_t p) { + if ( p.parent(2).isA() ) + p.parent(2).scope()->insert(d.id(), NodeRef(p.node)); + + if ( p.parent(1).isA() ) + p.parent(1).scope()->insert(d.id(), NodeRef(p.node)); + } + + void operator()(const declaration::LocalVariable& d, position_t p) { + if ( p.parent().isA() ) { + // Statement node may be replaced later, so insert an indirect + // reference to the local. + NodeRef x = NodeRef(p.parent()); + auto forward = + declaration::Forward([x]() -> Declaration { return *x->as().init(); }, d.meta()); + p.parent().scope()->insert(d.id(), Declaration(forward)); + return; + } + + if ( p.parent().isA() ) { + // Statement node may be replaced later, so insert an indirect + // reference to the local. + NodeRef x = NodeRef(p.parent()); + auto forward = + declaration::Forward([x]() -> Declaration { return *x->as().init(); }, d.meta()); + p.parent().scope()->insert(d.id(), Declaration(forward)); + return; + } + + p.parent().scope()->insert(d.id(), NodeRef(p.node)); + } + + void operator()(const expression::ListComprehension& e, position_t p) { + auto wrapper = type::Computed(NodeRef(p.node), [](auto n) { + const auto& lc = n.template as(); + if ( lc.input().type().template isA() ) + return lc.input().type(); + + return lc.input().type().iteratorType(true).dereferencedType(); + }); + + auto d = declaration::LocalVariable(e.id(), wrapper, {}, true, e.id().meta()); + p.node.scope()->insert(d.id(), Declaration(d)); + } + + void operator()(const statement::For& s, position_t p) { + auto wrapper = type::Computed(NodeRef(p.node), [](auto n) { + return n.template as().sequence().type().iteratorType(true).dereferencedType(); + }); + + auto d = declaration::LocalVariable(s.id(), wrapper, {}, true, s.id().meta()); + s.scope()->insert(d.id(), Declaration(d)); + } +}; + +struct VisitorPass2 : public visitor::PostOrder { + explicit VisitorPass2(Unit* unit) : unit(unit) {} + Unit* unit; + + void operator()(const Declaration& d, position_t p) { + if ( p.parent().isA() && d.id().namespace_().empty() ) + p.parent().scope()->insert(d.id(), NodeRef(p.node)); + } +}; + +struct VisitorPass3 : public visitor::PostOrder { + explicit VisitorPass3(Unit* unit) : unit(unit) {} + Unit* unit; + + std::pair> lookupType(Node* u, const ID& id) { + auto resolved = u->scope()->lookupAll(id); + + if ( resolved.empty() ) + return std::make_pair(false, std::nullopt); + + if ( resolved.size() == 1 ) { + auto& r = resolved.front(); + + if ( auto t = r.node->template tryAs() ) { + if ( t->type().isA() ) + return std::make_pair(false, r.node); + } + + u->setError(util::fmt("ID %s does not resolve to a type (but to %s)", id, r.node->typename_())); + return std::make_pair(true, std::nullopt); + } + + u->setError(util::fmt("type namespace %s is ambigious", id)); + return std::make_pair(true, std::nullopt); + } + + void operator()(const declaration::Function& f, position_t p) { + if ( f.linkage() == declaration::Linkage::Struct && ! f.function().isStatic() ) { + auto ns = f.id().namespace_(); + + if ( ns.empty() ) { + p.node.setError("method lacks a type namespace"); + return; + } + + for ( auto i = p.path.rbegin(); i != p.path.rend(); i++ ) { + auto [stop, node] = lookupType(&**i, ns); + + if ( stop ) + return; + + if ( ! node ) + continue; + + auto t = (*node)->as().type().as(); + auto fields = t.fields(f.id().local()); + + if ( fields.empty() ) { + p.node.setError(util::fmt("type %s does not have a method '%s'", ns, f.id().local())); + return; + } + + bool found = false; + for ( const auto& sf : fields ) { + auto sft = sf.type().tryAs(); + + if ( ! sft ) { + p.node.setError(util::fmt("%s is not a method", ID(ns, f.id().local()))); + return; + } + + if ( areEquivalent(*sft, f.function().type()) ) + found = true; + } + + if ( ! found ) { + p.node.setError( + util::fmt("type %s does not have a method '%s' matching the signature", ns, f.id().local())); + return; + } + + p.node.setScope((*node)->scope()); + return; + } + + p.node.setError(util::fmt("cannot resolve type namespace %s", ns)); + } + } +}; + +} // anonymous namespace + +void hilti::detail::resetNodes(Node* root) { + for ( const auto& i : hilti::visitor::PreOrder<>().walk(root) ) { + i.node.scope()->clear(); + i.node.clearError(); + } +} + +void hilti::detail::clearErrors(Node* root) { + for ( const auto& i : hilti::visitor::PreOrder<>().walk(root) ) + i.node.clearError(); +} + +void hilti::detail::buildScopes(const std::vector>& modules, Unit* unit) { + util::timing::Collector _("hilti/compiler/scope-builder"); + + // Need to run each phase on all modules first before proceeding to the + // next as they maybe be cross-module dependencies in later phases. + + for ( auto& [id, m] : modules ) { + auto v1 = VisitorPass1(unit); + for ( auto i : v1.walk(&*m) ) + v1.dispatch(i); + } + + for ( auto& [id, m] : modules ) { + auto v2 = VisitorPass2(unit); + for ( auto i : v2.walk(&*m) ) + v2.dispatch(i); + } + + for ( auto& [id, m] : modules ) { + auto v3 = VisitorPass3(unit); + for ( auto i : v3.walk(&*m) ) + v3.dispatch(i); + } +} diff --git a/hilti/src/compiler/visitors/validator.cc b/hilti/src/compiler/visitors/validator.cc new file mode 100644 index 000000000..2d341e5b0 --- /dev/null +++ b/hilti/src/compiler/visitors/validator.cc @@ -0,0 +1,457 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include + +using namespace hilti; +using util::fmt; + +namespace { + +struct Visitor : public visitor::PostOrder { + void preDispatch(const Node& n, int level) override { + // Validate that identifier names are not reused. + for ( const auto& [id, nodes] : n.scope()->items() ) { + if ( nodes.size() <= 1 ) + continue; + + const auto& firstNode = nodes.front(); + for ( auto it = std::next(nodes.begin()); it != nodes.end(); ++it ) { + const auto& node = *it; + + // We whitelist functions and import declarations as they can legitimately appear multiple times. + // To not permit shadowing of e.g., variable declarations with function declarations, we require nodes + // with identical names to have identical types unless we are in an `ImportedModule` declaration + // referring to a previously declared `Module` of the same name. + if ( node->isA() && node->typeid_() == firstNode->typeid_() ) + continue; + else if ( node->isA() && + (node->typeid_() == firstNode->typeid_() || firstNode->isA()) ) + continue; + + logger().error(fmt("redefinition of '%s' defined in %s", id, firstNode->location()), node->location()); + } + } + }; + + ////// Declarations + + void operator()(const declaration::Constant& n) { + if ( n.value().type().isWildcard() ) + logger().error("cannot use wildcard type for constants", n); + } + + void operator()(const declaration::LocalVariable& n) { + if ( ! type::isAllocable(n.type()) ) + logger().error(fmt("type '%s' cannot be used for variable declaration", n.type()), n); + + if ( n.type().isWildcard() ) + logger().error("cannot use wildcard type for variables", n); + + if ( ! n.typeArguments().empty() ) { + auto t = n.type(); + + if ( type::isReferenceType(t) ) + t = t.dereferencedType(); + + if ( ! t.isA() ) + logger().error("only struct types can have arguments", n); + } + } + + void operator()(const declaration::Parameter& n, const_position_t p) { + if ( ! type::isAllocable(n.type()) && n.type() != type::Any() ) + logger().error(fmt("type '%s' cannot be used for function parameter", n.type()), n); + + if ( n.type().isWildcard() ) { + if ( auto d = p.parent(3).tryAs() ) { + if ( ! AttributeSet::find(d->function().attributes(), "&cxxname") ) + logger().error(fmt("parameter '%s' cannot have wildcard type; only allowed with runtime library " + "functions declared with &cxxname", + n.id()), + n); + } + + if ( auto d = p.parent(4).tryAs() ) { + if ( ! AttributeSet::find(d->attributes(), "&cxxname") ) + logger().error(fmt("parameter '%s' cannot have wildcard type; only allowed with methods in runtime " + "library structs declared with &cxxname", + n.id()), + n); + } + } + } + + void operator()(const declaration::GlobalVariable& n) { + if ( ! type::isAllocable(n.type()) ) + logger().error(fmt("type '%s' cannot be used for variable declaration", n.type()), n); + + if ( n.type().isWildcard() ) + logger().error("cannot use wildcard type for variables", n); + + if ( auto args = n.typeArguments(); args.size() ) { + if ( ! n.type().isA() ) + logger().error("only struct types can have arguments", n); + } + } + + ////// Ctors + + void operator()(const ctor::Default& c, const_position_t p) {} + + void operator()(const ctor::List& n) { + auto t = n.elementType(); + + if ( ! n.value().empty() && t == type::unknown ) + logger().error("non-empty list cannot have unknown type", n); + } + + void operator()(const ctor::Null& c, const_position_t p) {} + + void operator()(const ctor::SignedInteger& n) { + auto [min, max] = util::signed_integer_range(n.type().width()); + + if ( n.value() < min || n.value() > max ) + logger().error("integer value out of range for type", n); + } + + void operator()(const ctor::Struct& n) { + // TODO(robin): . + } + + void operator()(const ctor::UnsignedInteger& n) { + auto [min, max] = util::unsigned_integer_range(n.type().width()); + + if ( n.value() < min || n.value() > max ) + logger().error("integer value out of range for type", n); + } + + void operator()(const ctor::Vector& n) { + auto t = n.elementType(); + + if ( ! n.value().empty() && t == type::unknown ) + logger().error("non-empty vector cannot have unknown type", n); + } + + ////// Expressions + + void operator()(const expression::Assign& n) { + if ( ! n.target().isLhs() ) + logger().error(fmt("cannot assign to expression: %s", to_node(n)), n); + } + + void operator()(const expression::ListComprehension& n) { + if ( ! type::isIterable(n.input().type()) ) + logger().error("input value not iterable", n); + } + + void operator()(const expression::Ternary& n) { + if ( ! hilti::type::sameExceptForConstness(n.true_().type(), n.false_().type()) ) + logger().error(fmt("types of alternatives do not match in ternary expression (%s vs. %s)", n.true_().type(), + n.false_().type()), + n); + } + + void operator()(const expression::TypeWrapped& n) { + if ( n.validateTypeMatch() && n.expression().type() != n.type() ) + logger().error(fmt("type mismatch, expression has type '%s', but expected '%s'", n.expression().type(), + n.type()), + n); + } + + void operator()(const expression::UnresolvedID& n, position_t p) { + if ( ! p.node.error() ) + logger().error("expression left unresolved", n); + } + + ////// Statements + + void operator()(const statement::For& n) { + if ( ! type::isIterable(n.sequence().type()) ) + logger().error("value not iterable", n); + } + + void operator()(const statement::If& n) { + if ( ! (n.init() || n.condition()) ) + logger().error("'if' header lacking both condition and declaration", n); + } + + void operator()(const statement::Break& n, const_position_t p) { + auto w = p.findParent(); + auto f = p.findParent(); + + if ( ! (f || w) ) { + logger().error("'break' outside of loop", n); + return; + } + } + + void operator()(const statement::Continue& n, const_position_t p) { + auto w = p.findParent(); + auto f = p.findParent(); + + if ( ! (f || w) ) { + logger().error("'continue' outside of loop", n); + return; + } + } + + void operator()(const statement::Return& n, const_position_t p) { + auto func = p.findParent(); + + if ( ! func ) { + logger().error("'return' outside of function", n); + return; + } + + if ( func->get().type().result().type() == type::Void() ) { + if ( n.expression() ) + logger().error("void function cannot return a value", n); + } + else { + if ( ! n.expression() ) + logger().error("function must return a value", n); + } + } + + void operator()(const statement::Switch& n) { + if ( n.cases().empty() ) + logger().error("switch statement has no cases", n); + } + + void operator()(const statement::Throw& n, const_position_t p) { + if ( auto e = n.expression() ) { + if ( ! e->type().isA() ) + logger().error("'throw' argument must be an exception"); + } + else { + if ( ! p.findParent() ) + logger().error("'throw' without expression can only be inside 'catch'", n); + } + } + + void operator()(const statement::try_::Catch& n) { + if ( n.parameter() && ! n.parameter()->type().isA() ) + logger().error("type of catch parameter must be an exception", n.meta().location()); + } + + void operator()(const statement::Try& n) { + if ( n.catches().empty() ) { + logger().error("'try' statement without any 'catch'"); + return; + } + + auto defaults = 0; + + for ( const auto& c : n.catches() ) { + if ( ! c.parameter() ) + ++defaults; + } + + if ( defaults > 1 ) + logger().error("'try` statement cannot have more than one defaullt `catch`"); + } + + void operator()(const statement::While& n) { + if ( ! (n.init() || n.condition()) ) + logger().error("'while' header lacking both condition and declaration"); + } + + void operator()(const expression::ResolvedOperator& n, const_position_t p) { + // We are running after both overload resolution and the + // apply-coercion pass, so operands types are ensured to be fine at + // this point, so only need to run operator-specific validation. + n.operator_().validate(n, p); + } + + void operator()(const expression::UnresolvedOperator& n, const_position_t p) { + if ( ! p.node.error() ) + logger().error("operator left unresolved", n); + } + + ////// Types + + void operator()(const type::Exception& n) { + if ( n.baseType() && ! n.baseType()->isA() ) + logger().error("exception's base type must be an exception type as well"); + } + + void operator()(const type::Function& n) { + if ( n.flavor() == type::function::Flavor::Hook ) { + auto r = n.result().type(); + if ( ! (r == type::Void() || r.isA()) ) + logger().error(fmt("hooks must have return type either void or optional")); + } + } + + void operator()(const type::SignedInteger& n) { + auto w = n.width(); + + if ( w != 8 && w != 16 && w != 32 && w != 64 && ! n.isWildcard() ) + logger().error(fmt("integer type's width must be one of 8/16/32/64, but is %d", n.width()), n); + } + + void operator()(const type::UnsignedInteger& n) { + auto w = n.width(); + + if ( w != 8 && w != 16 && w != 32 && w != 64 && ! n.isWildcard() ) + logger().error(fmt("integer type's width must be one of 8/16/32/64, but is %d", n.width()), n); + } + + void operator()(const type::Optional& n) { + if ( n.isWildcard() ) + return; + + if ( auto t = n.dereferencedType(); ! type::isAllocable(t) ) + logger().error(fmt("type %s cannot be used inside optional", t), n); + } + + void operator()(const type::StrongReference& n) { + if ( n.isWildcard() ) + return; + + if ( auto t = n.dereferencedType(); ! type::isAllocable(t) ) + logger().error(fmt("type %s is not allocable and can thus not be used with references", t), n); + } + + void operator()(const type::Result& n) { + if ( n.isWildcard() ) + return; + + if ( auto t = n.dereferencedType(); ! type::isAllocable(t) ) + logger().error(fmt("type %s cannot be used inside result", t), n); + } + + void operator()(const type::Struct& n) { + std::set seen; + + for ( const auto& f : n.fields() ) { + if ( seen.find(f.id()) != seen.end() && ! f.type().isA() ) + logger().error("duplicate attribute in struct type", n); + + seen.insert(f.id()); + + if ( f.isStatic() && f.default_() ) + logger().error("&default is currently not supported for static fields", n); + } + + for ( const auto& p : n.parameters() ) { + switch ( p.kind() ) { + case declaration::parameter::Kind::Copy: + case declaration::parameter::Kind::In: + // Nothing to check. + break; + + case declaration::parameter::Kind::InOut: + if ( ! type::isReferenceType(p.type()) ) + logger().error("only parameters of reference type can be 'inout' for struct parameters", n); + break; + + case declaration::parameter::Kind::Unknown: + logger().error("parameter kind unexpectedly not known", n); + break; + } + } + } + + void operator()(const type::Union& n) { + std::set seen; + + for ( const auto& f : n.fields() ) { + if ( seen.find(f.id()) != seen.end() ) + logger().error("duplicate attribute in union type", n); + + seen.insert(f.id()); + } + } + + void operator()(const type::Tuple& n) { + for ( const auto& t : n.types() ) { + if ( ! type::isAllocable(t) ) + logger().error(fmt("type '%s' cannot be used inside a tuple", t), n); + } + } + + void operator()(const type::UnresolvedID& n, position_t p) { + if ( ! p.node.error() ) + logger().error("ID left unresolved", n); + } + + void operator()(const type::WeakReference& n) { + if ( n.isWildcard() ) + return; + + if ( auto t = n.dereferencedType(); ! type::isAllocable(t) ) + logger().error(fmt("type %s is not allocable and can thus not be used with weak references", t), n); + } +}; + +} // anonymous namespace + +static int _validateAST(const Node& root, bool do_dispatch) { + util::timing::Collector _("hilti/compiler/validator"); + + std::unordered_set errors; + + auto v = Visitor(); + for ( auto i : v.walk(root) ) { + if ( auto e = i.node.error() ) { + // To avoid showing chains of errors triggering each other, we + // report only the 1st error per source location. (The more + // precise way would be: do not report current node if any child + // has an error, but this is easier and should be good enough for + // now.) + if ( errors.find(i.node.location()) == errors.end() ) { + logger().error(*e, i.node.errorContext(), i.node.location()); + errors.insert(i.node.location()); + } + } + + if ( do_dispatch ) + v.dispatch(i); + } + + return static_cast(errors.size()); +} + +void hilti::detail::validateAST(const Node& root) { _validateAST(root, true); } + +bool hilti::reportErrorsInAST(const Node& root) { return _validateAST(root, false) != 0; } + +int64_t detail::errorsInAST(const Node& n) { + int64_t errors = 0; + + for ( const auto& i : ::hilti::visitor::PreOrder<>().walk(n) ) { + if ( i.node.error() ) + ++errors; + } + + return errors; +} + +uint64_t detail::hashAST(const Node& n) { + uint64_t hash = 0; + + for ( const auto& i : ::hilti::visitor::PreOrder<>().walk(n) ) { + hash = (hash << 1U) | (hash >> 63U); + hash ^= static_cast(i.node.identity()); + } + + return hash; +} + +int64_t detail::unresolvedInAST(const Node& n) { + int64_t unresolved = 0; + + for ( const auto& i : ::hilti::visitor::PreOrder<>().walk(n) ) { + if ( i.node.isA<::hilti::expression::UnresolvedID>() || i.node.isA<::hilti::expression::UnresolvedOperator>() || + i.node.isA<::hilti::type::UnresolvedID>() ) + ++unresolved; + } + + return unresolved; +} diff --git a/hilti/src/config.cc.in b/hilti/src/config.cc.in new file mode 100644 index 000000000..a6df32b7b --- /dev/null +++ b/hilti/src/config.cc.in @@ -0,0 +1,102 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +using namespace hilti; + +const auto flatten = util::flattenParts; +const auto prefix = util::prefixParts; + +hilti::Configuration::Configuration() { + initLocation(util::currentExecutable().native()); +} + +void hilti::Configuration::initLocation(bool use_build_directory) { + init(use_build_directory); +} + +void hilti::Configuration::initLocation(const char* argv0) { + initLocation(std::string(argv0)); +} + +void hilti::Configuration::initLocation(const std::string_view& argv0) { +// auto exec = std::filesystem::absolute(std::filesystem::canonical(argv0)).native(); +// auto prefix = std::filesystem::absolute(std::filesystem::canonical("${CMAKE_BINARY_DIR}")).native(); + auto exec = std::filesystem::absolute(argv0).native(); + auto prefix = std::filesystem::absolute("${CMAKE_BINARY_DIR}").native(); + init(util::startsWith(exec, prefix)); +} + +void Configuration::init(bool use_build_directory) { + uses_build_directory = use_build_directory; + std::string installation_tag = (use_build_directory ? "BUILD" : "INSTALL"); + + cxx = "${CMAKE_CXX_COMPILER}"; + distbase = "${CMAKE_SOURCE_DIR}"; + install_prefix = "${CMAKE_INSTALL_PREFIX}"; + build_directory = "${CMAKE_BINARY_DIR}"; + lib_directory = (uses_build_directory ? "${PROJECT_BINARY_DIR}/lib" : "${CMAKE_INSTALL_FULL_LIBDIR}"); + hiltic = (uses_build_directory ? "${PROJECT_BINARY_DIR}/bin/hiltic" : "${CMAKE_INSTALL_PREFIX}/bin/hiltic"); + version_number = PROJECT_VERSION_NUMBER; + version_major = PROJECT_VERSION_MAJOR; + version_minor = PROJECT_VERSION_MINOR; + version_patch = PROJECT_VERSION_PATCH; + version_prerelease = PROJECT_VERSION_PRERELEASE; + version_string = PROJECT_VERSION_STRING_SHORT; + version_string_long = PROJECT_VERSION_STRING_LONG; + +#ifdef HILTI_HAVE_JIT + jit_enabled = true; +#else + jit_enabled = false; +#endif + + jit_clang_executable = "${CLANG_EXECUTABLE}"; + jit_clang_resource_dir = "${CLANG_RESOURCE_DIR}"; + jit_c_system_include_dirs = "${C_SYSTEM_INCLUDE_DIRS}"; + jit_cxx_system_include_dirs = "${CXX_SYSTEM_INCLUDE_DIRS}"; + + std::vector library_paths; + + if ( auto hilti_library_paths = std::getenv("HILTI_PATH") ) { + library_paths = + util::transform(hilti::rt::split(hilti_library_paths, ":"), [](auto s) { return std::string(s); }); + } + else { + library_paths = flatten({".", prefix("${HILTI_CONFIG_LIBRARY_DIRS}", "", installation_tag)}); + } + + hilti_library_paths = util::transform(library_paths, [](auto s) { return std::filesystem::path(s); }); + + runtime_cxx_flags_debug = flatten({ + prefix("${HILTI_CONFIG_RUNTIME_INCLUDE_DIRS_DEBUG}", "-I", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_CXX_FLAGS_DEBUG}", "", installation_tag) + }); + + runtime_cxx_flags_release = flatten({ + prefix("${HILTI_CONFIG_RUNTIME_INCLUDE_DIRS_RELEASE}", "-I", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_CXX_FLAGS_RELEASE}", "", installation_tag) + }); + + runtime_ld_flags_debug = flatten({ + prefix("${HILTI_CONFIG_RUNTIME_LIBRARY_DIRS_DEBUG}", "-L", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_LIBRARY_DIRS_DEBUG}", "-Wl,-rpath,", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_LIBRARIES_DEBUG}", "-l", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_LD_FLAGS_DEBUG}", "", installation_tag) + }); + + runtime_ld_flags_release = flatten({ + prefix("${HILTI_CONFIG_RUNTIME_LIBRARY_DIRS_RELEASE}", "-L", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_LIBRARY_DIRS_RELEASE}", "-Wl,-rpath,", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_LIBRARIES_RELEASE}", "-l", installation_tag), + prefix("${HILTI_CONFIG_RUNTIME_LD_FLAGS_RELEASE}", "", installation_tag) + }); +}; + +Configuration& hilti::configuration() { + static Configuration singleton; + return singleton; +} diff --git a/hilti/src/global.cc b/hilti/src/global.cc new file mode 100644 index 000000000..8607cf9c0 --- /dev/null +++ b/hilti/src/global.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include +#include + +using namespace hilti; + +void hilti::render(std::ostream& out, const Node& node, bool include_scopes) { + detail::renderNode(node, out, include_scopes); +} + +void hilti::render(logging::DebugStream stream, const Node& node, bool include_scopes) { + detail::renderNode(node, std::move(stream), include_scopes); +} + +#ifdef HILTI_HAVE_SANITIZER +// Prevent some errors from showing up, include some (presumably) false positives in LLVM +// because it wasn't built with sanitizer support. +// +// TODO(robin): Doesn't work on Linux to have this in shared library. The weak symbol in static ASAN runtime +// wins during linking. +extern "C" { +const char* __asan_default_options() { return "detect_container_overflow=0:detect_odr_violation=0"; } +} +#endif + +namespace hilti::logging::debug { +inline const DebugStream Resolver("resolver"); +} // namespace hilti::logging::debug + +std::pair>> hilti::detail::lookupID(const ID& id, const Node& n) { + auto resolved = n.scope()->lookupAll(id); + + if ( resolved.empty() ) { + auto err = result::Error(util::fmt("unknown ID '%s'", id)); + return std::make_pair(false, std::move(err)); + } + + if ( resolved.size() == 1 ) { + auto& r = resolved.front(); + + if ( ! r.node ) { + auto err = result::Error(util::fmt("internal error: scope's entry for ID '%s' is no longer valid", id)); + return std::make_pair(false, std::move(err)); + } + + if ( auto d = r.node->template tryAs() ) { + if ( r.external && d->linkage() != declaration::Linkage::Public ) { + bool ok = false; + + // We allow access to types (and type-derived constants) to + // make it less cumbersome to define external hooks. + + if ( d->isA() ) + ok = true; + + if ( auto c = d->tryAs() ) { + if ( auto ctor = c->value().tryAs(); ctor && ctor->ctor().isA() ) + ok = true; + } + + if ( ! ok ) { + auto err = result::Error(util::fmt("'%s' has not been declared public", id)); + return std::make_pair(true, std::move(err)); + } + } + + HILTI_DEBUG(logging::debug::Resolver, util::fmt("resolved ID %s (%s) to %s", id, id.meta().location(), + resolved.front().node->render())); + + auto x = std::make_pair(resolved.front().node, ID(resolved.front().qualified)); + return std::make_pair(true, std::move(x)); + } + + logger().internalError(util::fmt("ID '%s' resolved to something else than a declaration (%s)", id, + resolved.front().node->typename_()), + resolved.front().node->meta().location()); + } + + auto err = result::Error(util::fmt("ID '%s' is ambigious", id)); + return std::make_pair(true, std::move(err)); +} diff --git a/hilti/src/rt/backtrace.cc b/hilti/src/rt/backtrace.cc new file mode 100644 index 000000000..c9aa24baf --- /dev/null +++ b/hilti/src/rt/backtrace.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include + +using namespace hilti::rt; + +Backtrace::Backtrace() { +#ifdef HILTI_HAVE_BACKTRACE + void* callstack[128]; + int frames = ::backtrace(callstack, 128); + + char** strings; + + strings = backtrace_symbols(callstack, frames); + assert(strings); + + for ( auto i = 0; i < frames; i++ ) { + auto p1 = strchr(strings[i], '('); + auto p2 = p1 ? strchr(p1, '+') : nullptr; + auto p3 = p2 ? strchr(p2, ')') : nullptr; + if ( p1 && p2 && p3 ) { + *p2 = '\0'; + _backtrace.push_back(fmt("# %s %s", p3 + 2, demangle(p1 + 1))); + } + else + _backtrace.push_back(fmt("# %s", strings[i])); + } + + free(strings); // NOLINT +#else + _backtrace.push_back("# "); +#endif +} diff --git a/hilti/src/rt/configuration.cc b/hilti/src/rt/configuration.cc new file mode 100644 index 000000000..0a0abe4e3 --- /dev/null +++ b/hilti/src/rt/configuration.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include + +#include +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +Configuration::Configuration() { + auto x = getenv("HILTI_DEBUG"); + debug_streams = (x ? x : ""); +} + +Configuration configuration::get() { + if ( ! globalState()->configuration ) + globalState()->configuration = std::make_unique(); + + return *globalState()->configuration; +} + +void configuration::set(Configuration cfg) { + if ( isInitialized() ) + hilti::rt::fatalError("attempt to change configuration after library has already been initialized"); + + *globalState()->configuration = std::move(cfg); +} diff --git a/hilti/src/rt/context.cc b/hilti/src/rt/context.cc new file mode 100644 index 000000000..418c507ed --- /dev/null +++ b/hilti/src/rt/context.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +namespace hilti::rt::context::detail { +// Not part of global state, it's per thread. +thread_local Context* __current = nullptr; +Context*& current() { return __current; } +} // namespace hilti::rt::context::detail + +Context::Context(vthread::ID vid) : vid(vid) { + if ( vid == vthread::Master ) { + HILTI_RT_DEBUG("libhilti", "creating master context"); + // Globals for the master context are initialized separately as we + // may not have the state available yet. + return; + } + + for ( const auto& m : globalState()->hilti_modules ) { + if ( m.init_globals ) + (*m.init_globals)(this); + } +} + +Context::~Context() { + if ( vid == vthread::Master ) { + HILTI_RT_DEBUG("libhilti", "destroying master context"); + } + else { + HILTI_RT_DEBUG("libhilti", fmt("destroying context for vid %" PRIu64, vid)); + } +} + +Context* context::detail::master() { return globalState()->master_context.get(); } diff --git a/hilti/src/rt/debug-logger.cc b/hilti/src/rt/debug-logger.cc new file mode 100644 index 000000000..9cc99e07f --- /dev/null +++ b/hilti/src/rt/debug-logger.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +#include +#include +#include + +#include + +using namespace hilti::rt; + +detail::DebugLogger::DebugLogger(std::filesystem::path output) : _path(std::move(output)) {} + +void detail::DebugLogger::enable(const std::string& streams) { + for ( auto s : split(streams, ":") ) + _streams[std::string(trim(s))] = 0; +} + +void detail::DebugLogger::print(const std::string& stream, const std::string& msg) { + if ( _path.empty() ) + return; + + auto i = _streams.find(stream); + + if ( i == _streams.end() ) + return; + + if ( ! _output ) { + auto mode = std::ios::out; + + if ( _path == "/dev/stdout" || _path == "/dev/stderr" ) + mode |= std::ios::app; + else + mode |= std::ios::trunc; + + std::ofstream out(_path, mode); + + if ( ! out.is_open() ) + fatalError(fmt("libhilti: cannot open file '%s' for debug output", _path)); + + _output = std::move(out); + } + + auto indent = std::string(i->second * 2, ' '); + (*_output) << fmt("[%s] %s%s", stream, indent, msg) << std::endl; +} diff --git a/hilti/src/rt/exception.cc b/hilti/src/rt/exception.cc new file mode 100644 index 000000000..99cac19bb --- /dev/null +++ b/hilti/src/rt/exception.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include + +using namespace hilti::rt; + +static void printException(const std::string& msg, const Exception& e, std::ostream& out) { + out << "[libhilti] " << msg << " " << demangle(typeid(e).name()) << ": " << e.what() << std::endl; + + if ( e.backtrace().empty() || ! configuration::get().show_backtraces ) + return; + + out << "[libhilti] backtrace:\n"; + + for ( const auto& s : e.backtrace() ) + out << "[libhilti] " << s << "\n"; +} + +Exception::Exception(const std::string& what, std::string_view desc, std::string_view location) + : std::runtime_error(what), _description(desc), _location(location) { + if ( configuration::get().abort_on_exceptions && ! detail::globalState()->disable_abort_on_exceptions ) { + // TODO(robin): This will print the name of the base class (Exception), not + // the derived exception, because we're in the construtor. Is there + // another way to get the final name? + printException("Aborting on exception", *this, std::cerr); + abort(); + } +} + +Exception::Exception(const std::string& desc) + : Exception(debug::location() ? fmt("%s (%s)", desc, debug::location()) : desc, desc, + debug::location() ? debug::location() : "") {} + +Exception::Exception(std::string_view desc, std::string_view location) + : Exception(fmt("%s (%s)", desc, location), desc, location) {} + +Exception::~Exception() = default; + +exception::DisableAbortOnExceptions::DisableAbortOnExceptions() { + detail::globalState()->disable_abort_on_exceptions++; +} + +exception::DisableAbortOnExceptions::~DisableAbortOnExceptions() { + detail::globalState()->disable_abort_on_exceptions--; +} + + +void exception::printUncaught(const Exception& e) { printException("Uncaught exception", e, std::cerr); } + +void exception::printUncaught(const Exception& e, std::ostream& out) { printException("Uncaught exception", e, out); } diff --git a/hilti/src/rt/fiber.cc b/hilti/src/rt/fiber.cc new file mode 100644 index 000000000..5e3c2e371 --- /dev/null +++ b/hilti/src/rt/fiber.cc @@ -0,0 +1,287 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#ifdef _FORTIFY_SOURCE +// Disable in this file, the longjmps can cause false positives. +#undef _FORTIFY_SOURCE +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HILTI_HAVE_SANITIZER +#include +#endif + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +static const unsigned int StackSize = 327680; +static const unsigned int CacheSize = 100; + +const void* _main_thread_bottom = nullptr; +std::size_t _main_thread_size = 0; + +extern "C" { + +void _Trampoline(unsigned int y, unsigned int x) { + // Magic from from libtask/task.c to turn the two words back into a pointer. + unsigned long z; // NOLINT + z = (x << 16U); + z <<= 16U; + z |= y; + auto fiber = (Fiber*)z; // NOLINT + + fiber->_finishSwitchFiber("trampoline-init"); + + // Via recycling a fiber can run an arbitrary number of user jobs. So + // this trampoline is really a loop that yields after it has finished its + // function, and expects a new run function once it's resumed. + + while ( true ) { + assert(fiber->_state == Fiber::State::Running); + + if ( ! _setjmp(fiber->_trampoline) ) { + // In parent. + try { + fiber->_result = (*fiber->_function)(fiber); + } catch ( ... ) { + HILTI_RT_DEBUG("fibers", fmt("[%p] got exception, forwarding", fiber)); + fiber->_exception = std::current_exception(); + } + + fiber->_state = Fiber::State::Finished; + } + + if ( ! _setjmp(fiber->_fiber) ) { + fiber->_function = {}; + fiber->_state = Fiber::State::Idle; + fiber->_startSwitchFiber("trampoline"); + _longjmp(fiber->_parent, 1); + } + + fiber->_finishSwitchFiber("trampoline-loop"); + } +} + +Fiber::Fiber() { + HILTI_RT_DEBUG("fibers", fmt("allocating new fiber %p", this)); + + if ( getcontext(&_uctx) < 0 ) + internalError("fiber: getcontext failed"); + + _uctx.uc_link = nullptr; + _uctx.uc_stack.ss_size = StackSize; + _uctx.uc_stack.ss_sp = new char[StackSize]; + _uctx.uc_stack.ss_flags = 0; + + // Magic from from libtask/task.c to turn the pointer into two words. + // TODO(robin): Probably not portable ... + unsigned long z = (unsigned long)this; // NOLINT + unsigned int y = z; + z >>= 16U; + unsigned int x = (z >> 16U); + + makecontext(&_uctx, (void (*)())_Trampoline, 2, y, x); // NOLINT (cppcoreguidelines-pro-type-cstyle-cast) + + ++_total_fibers; + ++_current_fibers; + + if ( _current_fibers > _max_fibers ) + _max_fibers = _current_fibers; +} +} + +class AbortException : public std::exception {}; + +Fiber::~Fiber() { + HILTI_RT_DEBUG("fibers", fmt("deleting fiber %p", this)); + + delete[] static_cast(_uctx.uc_stack.ss_sp); + --_current_fibers; +} + +void Fiber::run() { + auto init = (_state == State::Init); + + if ( _state != State::Aborting ) + _state = State::Running; + + if ( ! _setjmp(_parent) ) { + _startSwitchFiber("run", _uctx.uc_stack.ss_sp, _uctx.uc_stack.ss_size); + + if ( init ) + setcontext(&_uctx); + else { + _longjmp(_fiber, 1); + } + + internalError("fiber: unreachable reached"); + } + + _finishSwitchFiber("run"); + + switch ( _state ) { + case State::Yielded: + case State::Idle: return; + + default: internalError("fiber: unexpected case"); + } +} + +void Fiber::yield() { + assert(_state == State::Running); + + if ( ! _setjmp(_fiber) ) { + _state = State::Yielded; + _startSwitchFiber("yield"); + _longjmp(_parent, 1); + } + + _finishSwitchFiber("yield"); + + if ( _state == State::Aborting ) + throw AbortException(); +} + +void Fiber::resume() { + assert(_state == State::Yielded); + return run(); +} + +void Fiber::abort() { + assert(_state == State::Yielded); + _state = State::Aborting; + return run(); +} + +std::unique_ptr Fiber::create() { + if ( ! globalState()->fiber_cache.empty() ) { + auto f = std::move(globalState()->fiber_cache.back()); + globalState()->fiber_cache.pop_back(); + HILTI_RT_DEBUG("fibers", fmt("reusing fiber %p form cache", f.get())); + return f; + } + + return std::make_unique(); +} + +void Fiber::destroy(std::unique_ptr f) { + if ( globalState()->fiber_cache.size() < CacheSize ) { + HILTI_RT_DEBUG("fibers", fmt("putting fiber %p back into cache", f.get())); + globalState()->fiber_cache.push_back(std::move(f)); + return; + } + + HILTI_RT_DEBUG("fibers", fmt("cache size exceeded, deleting finished fiber %p", f.get())); + f.reset(); +} + +void Fiber::reset() { + globalState()->fiber_cache.clear(); + _total_fibers = 0; + _current_fibers = 0; + _max_fibers = 0; +} + +void Fiber::_startSwitchFiber(const char* tag, const void* stack_bottom, size_t stack_size) { +#ifdef HILTI_HAVE_SANITIZER + if ( ! stack_bottom ) { + stack_bottom = _asan.prev_bottom; + stack_size = _asan.prev_size; + } + + HILTI_RT_DEBUG("fibers", fmt("[%p/%s/asan] start_switch_fiber %p/%p (fake_stack=%p)", this, tag, stack_bottom, + stack_size, &_asan.fake_stack)); + __sanitizer_start_switch_fiber(&_asan.fake_stack, stack_bottom, stack_size); +#else + HILTI_RT_DEBUG("fibers", fmt("[%p/%s] finish_switch_fiber", this, tag)); +#endif +} + +void Fiber::_finishSwitchFiber(const char* tag) { +#ifdef HILTI_HAVE_SANITIZER + __sanitizer_finish_switch_fiber(_asan.fake_stack, &_asan.prev_bottom, &_asan.prev_size); + HILTI_RT_DEBUG("fibers", fmt("[%p/%s/asan] finish_switch_fiber %p/%p (fake_stack=%p)", this, tag, _asan.prev_bottom, + _asan.prev_size, _asan.fake_stack)); +#else + HILTI_RT_DEBUG("fibers", fmt("[%p/%s] finish_switch_fiber", this, tag)); +#endif +} + +void Resumable::run() { + checkFiber("run"); + + auto old = context::detail::get()->resumable; + context::detail::get()->resumable = handle(); + _fiber->run(); + context::detail::get()->resumable = old; + + yielded(); +} + +void Resumable::resume() { + checkFiber("resume"); + + auto old = context::detail::get()->resumable; + context::detail::get()->resumable = handle(); + _fiber->resume(); + context::detail::get()->resumable = old; + + yielded(); +} + +void Resumable::abort() { + if ( ! _fiber ) + return; + + auto old = context::detail::get()->resumable; + context::detail::get()->resumable = handle(); + _fiber->abort(); + context::detail::get()->resumable = old; + + _result = false; +} + +void Resumable::yielded() { + if ( auto e = _fiber->exception() ) { + HILTI_RT_DEBUG("fibers", fmt("rethrowing exception after fiber %p yielded", _fiber.get())); + + _result = false; // just make sure optional is set. + detail::Fiber::destroy(std::move(_fiber)); + _fiber = nullptr; + std::rethrow_exception(e); + return; + } + + if ( auto&& r = _fiber->result() ) { + _result = std::move(r); + detail::Fiber::destroy(std::move(_fiber)); + _fiber = nullptr; + return; + } +} + +void detail::yield() { + auto r = context::detail::get()->resumable; + + if ( ! r ) + throw Exception("'yield' in non-suspendable context"); + + r->yield(); + context::detail::get()->resumable = r; +} + +Fiber::Statistics Fiber::statistics() { + Statistics stats{.total = _total_fibers, + .current = _current_fibers, + .cached = globalState()->fiber_cache.size(), + .max = _max_fibers}; + + return stats; +} diff --git a/hilti/src/rt/global-state.cc b/hilti/src/rt/global-state.cc new file mode 100644 index 000000000..e6e2b822b --- /dev/null +++ b/hilti/src/rt/global-state.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +// Not memory-managed through smart pointer, need to control when we release +// it. +GlobalState* detail::__global_state = nullptr; + +GlobalState* detail::createGlobalState() { + __global_state = new GlobalState(); // NOLINT (cppcoreguidelines-owning-memory) + return __global_state; +} + +GlobalState::~GlobalState() { HILTI_RT_DEBUG("libhilti", "destroying global state"); } diff --git a/hilti/src/rt/hilti.cc b/hilti/src/rt/hilti.cc new file mode 100644 index 000000000..8f413ff21 --- /dev/null +++ b/hilti/src/rt/hilti.cc @@ -0,0 +1,2 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// Functions made available to HILTI programs. diff --git a/hilti/src/rt/init.cc b/hilti/src/rt/init.cc new file mode 100644 index 000000000..59f202806 --- /dev/null +++ b/hilti/src/rt/init.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +void hilti::rt::init() { + if ( globalState()->runtime_is_initialized ) + return; + + if ( ! setlocale(LC_CTYPE, "") ) + fatalError("cannot set locale"); + + if ( ! globalState()->configuration ) + globalState()->configuration = std::make_unique(); + + if ( auto debug_out = globalState()->configuration->debug_out ) + globalState()->debug_logger = std::make_unique(*debug_out); + else + globalState()->debug_logger = std::make_unique("/dev/stderr"); + + globalState()->debug_logger->enable(globalState()->configuration->debug_streams); + + HILTI_RT_DEBUG("libhilti", "initializing runtime"); + + globalState()->master_context = std::make_unique(vthread::Master); + context::detail::set(globalState()->master_context.get()); + + for ( const auto& m : globalState()->hilti_modules ) { + if ( m.init_globals ) { + HILTI_RT_DEBUG("libhilti", fmt("initializing globals for module %s", m.name)); + (*m.init_globals)(context::detail::master()); + } + } + + for ( const auto& m : globalState()->hilti_modules ) { + if ( m.init_module ) { + HILTI_RT_DEBUG("libhilti", fmt("executing initialization code for module %s", m.name)); + (*m.init_module)(); + } + } + + globalState()->runtime_is_initialized = true; +} + +void hilti::rt::done() { + if ( ! globalState() ) + return; + + HILTI_RT_DEBUG("libhilti", "shutting down runtime"); + + delete __global_state; // NOLINT (cppcoreguidelines-owning-memory) + __global_state = nullptr; +} + +bool hilti::rt::isInitialized() { return globalState()->runtime_is_initialized; } + +void hilti::rt::detail::registerModule(HiltiModule module) { + HILTI_RT_DEBUG("libhilti", fmt("registering module %s", module.name)); + + if ( module.globals_idx ) + *module.globals_idx = globalState()->hilti_modules.size(); + + globalState()->hilti_modules.emplace_back(std::move(module)); +} diff --git a/hilti/src/rt/linker.cc b/hilti/src/rt/linker.cc new file mode 100644 index 000000000..d5fcd8433 --- /dev/null +++ b/hilti/src/rt/linker.cc @@ -0,0 +1 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. diff --git a/hilti/src/rt/logging.cc b/hilti/src/rt/logging.cc new file mode 100644 index 000000000..ac88950a0 --- /dev/null +++ b/hilti/src/rt/logging.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::detail; + +#include +#include + +#include + +using namespace hilti::rt; + +void hilti::rt::internalError(const std::string& msg) { + std::cerr << fmt("[libhilti] Internal error: %s", msg) << std::endl; + abort_with_backtrace(); +} + +void hilti::rt::fatalError(const std::string& msg) { + std::cerr << fmt("[libhilti] Fatal error: %s", msg) << std::endl; + exit(1); +} + +void hilti::rt::warning(const std::string& msg) { std::cerr << fmt("[libhilti] Warning: %s", msg) << std::endl; } diff --git a/hilti/src/rt/main.cc b/hilti/src/rt/main.cc new file mode 100644 index 000000000..356e834b7 --- /dev/null +++ b/hilti/src/rt/main.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +/// +/// Implementation of main() which directly executes Main::run(). +/// + +#include + +#include + +#include + +static struct option long_options[] = {{nullptr, 0, nullptr, 0}}; + +static void usage(const char* prog) { + std::cerr << hilti::rt:: + fmt("%s: This is a default entry point provided by the HILTI runtime library that initializes any " + "available modules.\n", + prog); +} + +namespace hilti { +int main(int argc, char** argv); +} // namespace hilti + +// Top-level entry point generated by HILTI compiler. We declare our +// implementation weak so any external implementation will override it. +__attribute__((weak)) int hilti::main(int argc, char** argv) { + auto config = hilti::rt::configuration::get(); + + while ( true ) { + int c = getopt_long(argc, argv, "h", long_options, nullptr); + + if ( c == -1 ) + break; + + switch ( c ) { // NOLINT + case 'h': usage(argv[0]); break; + default: usage(argv[0]); exit(1); + } + } + + if ( optind != argc ) + usage(argv[0]); + + hilti::rt::configuration::set(config); + hilti::rt::init(); + + return 0; +} + +__attribute__((weak)) int main(int argc, char** argv) { return hilti::main(argc, argv); } diff --git a/hilti/src/rt/tests/address.cc b/hilti/src/rt/tests/address.cc new file mode 100644 index 000000000..66f150bdf --- /dev/null +++ b/hilti/src/rt/tests/address.cc @@ -0,0 +1,108 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include + +#include + +#include + +using namespace hilti::rt; + +std::ostream& operator<<(std::ostream& out, const in_addr& addr) { return out << Address(addr); } + +std::ostream& operator<<(std::ostream& out, const in6_addr& addr) { return out << Address(addr); } + +static auto make_in6_addr(const char* d) { + auto addr = std::make_unique<::in6_addr>(); + REQUIRE(::inet_pton(AF_INET6, d, addr.get())); + return addr; +} + +static auto make_in_addr(const char* d) { + auto addr = std::make_unique<::in_addr>(); + REQUIRE(::inet_aton(d, addr.get())); + return addr; +} + +static bool operator==(const in_addr& a1, const in_addr& a2) { return a1.s_addr == a2.s_addr; } + +static bool operator!=(const in_addr& a1, const in_addr& a2) { return ! (a1 == a2); } + +static bool operator==(const in6_addr& a1, const in6_addr& a2) { + for ( auto i = 0; i < 8; ++i ) { + if ( a1.s6_addr[i] != a2.s6_addr[i] ) + return false; + } + + return true; +} + +static bool operator!=(const in6_addr& a1, const in6_addr& a2) { return ! (a1 == a2); } + +TEST_CASE("Address") { + SUBCASE("conversions to and from `std::string`") { + CHECK(std::string(Address("1.2.3.4")) == "1.2.3.4"); + CHECK(std::string(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348")) == "2001:db8:85a3:8d3:1319:8a2e:370:7348"); + + CHECK_THROWS(Address("example.com")); + CHECK_THROWS(Address("-1234567890")); + CHECK_THROWS(Address("-2001:db8:85a3:8d3:1319:8a2e:370:7348")); + } + + SUBCASE("constructs from an `::in_addr4`") { CHECK(std::string(Address(*make_in_addr("1.2.3.4"))) == "1.2.3.4"); } + + SUBCASE("constructs from an `::in6_addr`") { + CHECK(std::string(Address(*make_in6_addr("::4996:2d2:0:0:4996:2d2"))) == "::4996:2d2:0:0:4996:2d2"); + } + + SUBCASE("constructs from binary represenation of an IPv4 address") { + CHECK(std::string(Address(1234567890)) == "73.150.2.210"); + } + + SUBCASE("constructs from binary represenation of an IPv6 address") { + CHECK(std::string(Address(1234567890, 1234567890)) == "::4996:2d2:0:0:4996:2d2"); + } + + SUBCASE("family") { + CHECK(Address().family() == AddressFamily::IPv4); // Default address of 0.0.0.0 is IPv4. + CHECK(Address("1.2.3.4").family() == AddressFamily::IPv4); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").family() == AddressFamily::IPv6); + CHECK(Address("::ffff:1.2.3.4").family() == AddressFamily::IPv6); + } + + SUBCASE("mask") { + CHECK(Address("9.9.9.9").mask(0) == Address("0.0.0.0")); + CHECK(Address("9.9.9.9").mask(48) == Address("0.0.0.0")); + CHECK(Address("9.9.9.9").mask(96) == Address("0.0.0.0")); + CHECK(Address("9.9.9.9").mask(104) == Address("9.0.0.0")); + CHECK(Address("9.9.9.9").mask(112) == Address("9.9.0.0")); + CHECK(Address("9.9.9.9").mask(112) == Address("9.9.0.0")); + CHECK(Address("9.9.9.9").mask(120) == Address("9.9.9.0")); + CHECK(Address("9.9.9.9").mask(128) == Address("9.9.9.9")); + + // TODO(bbannier): An IPv6 address with a zero mask should still be an IPv6 address. + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(0) == Address("0.0.0.0")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(16) == Address("2001::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(32) == Address("2001:db8::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(48) == Address("2001:db8:85a3::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(64) == Address("2001:db8:85a3:8d3::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(80) == Address("2001:db8:85a3:8d3:1319::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(96) == Address("2001:db8:85a3:8d3:1319:8a2e::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(112) == + Address("2001:db8:85a3:8d3:1319:8a2e:370::")); + CHECK(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").mask(128) == + Address("2001:db8:85a3:8d3:1319:8a2e:370:7348")); + } + + SUBCASE("asInAddr") { + CHECK(std::get(Address().asInAddr()) == *make_in_addr("0.0.0.0")); + CHECK(std::get(Address("1.2.3.4").asInAddr()) == *make_in_addr("1.2.3.4")); + CHECK(std::get(Address("1.2.3.4").asInAddr()) != *make_in_addr("0.0.0.0")); + CHECK(std::get(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").asInAddr()) != + *make_in6_addr("2001::")); + CHECK(std::get(Address("2001:db8:85a3:8d3:1319:8a2e:370:7348").asInAddr()) == + *make_in6_addr("2001:db8:85a3:8d3:1319:8a2e:370:7348")); + } +} diff --git a/hilti/src/rt/tests/fiber.cc b/hilti/src/rt/tests/fiber.cc new file mode 100644 index 000000000..57cb7bc5f --- /dev/null +++ b/hilti/src/rt/tests/fiber.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +#include +#include + +class TestDtor { //NOLINT +public: + explicit TestDtor(std::string& c) : c(c) { c += "ctor"; } //NOLINT + ~TestDtor() { c += "dtor"; } + std::string& c; +}; + + +TEST_CASE("init") { hilti::rt::init(); } + +TEST_CASE("execute-void") { + std::string x; + std::string c; + + auto f = [&](hilti::rt::resumable::Handle* r) { + TestDtor t(c); + x = "Hello from fiber!"; + }; + + auto r = hilti::rt::fiber::execute(f); + REQUIRE(r); + CHECK(x == "Hello from fiber!"); + CHECK(c == "ctordtor"); +} + +TEST_CASE("execute-result") { + std::string x; + std::string c; + + auto f = [&](hilti::rt::resumable::Handle* r) { + TestDtor t(c); + x = "Hello from fiber!"; + return x; + }; + + auto r = hilti::rt::fiber::execute(f); + REQUIRE(r); + REQUIRE(x == "Hello from fiber!"); + REQUIRE(r.get() == "Hello from fiber!"); + REQUIRE(c == "ctordtor"); +} + +TEST_CASE("resume-void") { + std::string x; + std::string c; + + auto f = [&](hilti::rt::resumable::Handle* r) { + TestDtor t1(c); + x = "Hello"; + r->yield(); + TestDtor t2(c); + x += "from"; + r->yield(); + x += "fiber"; + r->yield(); + x += "!"; + }; + + auto r = hilti::rt::fiber::execute(f); + REQUIRE(! r); + + x += " "; + r.resume(); + REQUIRE(! r); + + x += " "; + r.resume(); + REQUIRE(! r); + + x += " "; + r.resume(); + REQUIRE(r); + REQUIRE(x == "Hello from fiber !"); + REQUIRE(c == "ctorctordtordtor"); +} + +TEST_CASE("resume-result") { + std::string c; + + auto f = [&](hilti::rt::resumable::Handle* r) { + TestDtor t(c); + std::string x; + x += "Hello"; + r->yield(); + x += " from"; + r->yield(); + x += " fiber"; + r->yield(); + x += "!"; + return x; + }; + + auto r = hilti::rt::fiber::execute(f); + REQUIRE(! r); + + r.resume(); + REQUIRE(! r); + + r.resume(); + REQUIRE(! r); + + r.resume(); + REQUIRE(r); + REQUIRE(r.get() == "Hello from fiber!"); + REQUIRE(c == "ctordtor"); +} + +TEST_CASE("exception") { + std::string x; + std::string c1; + std::string c2; + + auto f1 = [&](hilti::rt::resumable::Handle* r) { + TestDtor t(c1); + x = "Hello"; + throw std::runtime_error("kaputt"); + x += " from fiber!"; + }; + + try { + auto r = hilti::rt::fiber::execute(f1); + REQUIRE(false); + } catch ( const std::exception& e ) { + REQUIRE(e.what() == std::string("kaputt")); + // REQUIRE(r); + REQUIRE(x == "Hello"); + REQUIRE(c1 == "ctordtor"); + } + + auto f2 = [&](hilti::rt::resumable::Handle* r) { + TestDtor t(c2); + x = "Hello"; + r->yield(); + x += " from"; + throw std::runtime_error("kaputt"); + x += " fiber!"; + }; + + auto r2 = hilti::rt::fiber::execute(f2); + REQUIRE(! r2); + + REQUIRE_THROWS_WITH(r2.resume(), "kaputt"); + REQUIRE(r2); + REQUIRE(x == "Hello from"); + REQUIRE(c2 == "ctordtor"); +} + +TEST_CASE("abort") { + std::string x; + std::string c; + + auto f = [&](hilti::rt::resumable::Handle* r) { + TestDtor t(c); + x = "Hello"; + r->yield(); + x += " from fiber!"; + }; + + auto r = hilti::rt::fiber::execute(f); + REQUIRE(! r); + REQUIRE(x == "Hello"); + REQUIRE(c == "ctor"); + + r.abort(); + REQUIRE(r); + REQUIRE(x == "Hello"); + REQUIRE(c == "ctordtor"); +} + +TEST_CASE("stats") { + hilti::rt::detail::Fiber::reset(); // reset cache and counters + + auto f = [&](hilti::rt::resumable::Handle* r) { r->yield(); }; + + auto r1 = hilti::rt::fiber::execute(f); + auto r2 = hilti::rt::fiber::execute(f); + r2.resume(); + REQUIRE(r2); + + auto r3 = hilti::rt::fiber::execute(f); + + r1.resume(); + REQUIRE(r1); + + auto stats = hilti::rt::detail::Fiber::statistics(); + REQUIRE(stats.total == 2); + REQUIRE(stats.current == 2); + REQUIRE(stats.cached == 1); + REQUIRE(stats.max == 2); + + r3.resume(); + REQUIRE(r3); + + stats = hilti::rt::detail::Fiber::statistics(); + REQUIRE(stats.total == 2); + REQUIRE(stats.current == 2); + REQUIRE(stats.cached == 2); + REQUIRE(stats.max == 2); +} diff --git a/hilti/src/rt/tests/main.cc b/hilti/src/rt/tests/main.cc new file mode 100644 index 000000000..ce23c64db --- /dev/null +++ b/hilti/src/rt/tests/main.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include diff --git a/hilti/src/rt/tests/reference.cc b/hilti/src/rt/tests/reference.cc new file mode 100644 index 000000000..2e766a832 --- /dev/null +++ b/hilti/src/rt/tests/reference.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +#include +#include + +using namespace hilti::rt; + +TEST_CASE("value-reference-int") { + using T = int; + + ValueReference x1; + REQUIRE(*x1 == 0); + + ValueReference x2(42); + REQUIRE(*x2 == 42); + + ValueReference x3(x2); + REQUIRE(*x3 == 42); + + x3 = 21; + REQUIRE(*x3 == 21); + REQUIRE(*x2 == 42); + + ValueReference x4(std::move(x3)); + REQUIRE(*x4 == 21); + REQUIRE(x3.isNull()); + + ValueReference x5; + x5 = std::move(x4); + REQUIRE(*x5 == 21); + REQUIRE(x4.isNull()); +} + +struct T : public hilti::rt::trait::isStruct, hilti::rt::Controllable { + int x; + + void foo(int y) { + // Ensure we can reconstruct a value ref from "this". + auto self = ValueReference::self(this); + REQUIRE(x == y); + REQUIRE(self->x == y); + } +}; + +namespace hilti::rt::detail::adl { + +inline std::string to_string(int x, tag /*unused*/) { return hilti::rt::fmt("%d", x); } + +inline std::string to_string(const T& x, tag /*unused*/) { return hilti::rt::fmt("x=%d", x.x); } + +} // namespace hilti::rt::detail::adl + +TEST_CASE("value-reference-struct") { + ValueReference x1; + REQUIRE(x1->x == 0); + + T t; + t.x = 42; + ValueReference x2(t); + REQUIRE(x2->x == 42); + + x2->x = 21; + x2->foo(21); + + x2->x = 42; + x2->foo(42); +} + +TEST_CASE("value-reference-struct-self") { + T x1; + + auto self = ValueReference::self(&x1); + + self->x = 42; + REQUIRE(self->x == 42); + REQUIRE(x1.x == 42); + + REQUIRE_THROWS(StrongReference{self}); + REQUIRE_THROWS(WeakReference{self}); +} + + +TEST_CASE("strong-reference") { + using T = int; + + StrongReference x0; + REQUIRE(! x0); + + StrongReference x1(42); + REQUIRE(x1); + REQUIRE(*x1 == 42); + + StrongReference x2(x1); + REQUIRE(x2); + REQUIRE(*x2 == 42); + + *x1 = 21; + REQUIRE(*x1 == 21); + REQUIRE(*x2 == 21); + + ValueReference v1{1}; + ValueReference v2{2}; + + x1 = v1; + x2 = x1; + v1 = v2; + + REQUIRE(*x1 == 2); + REQUIRE(*v1 == 2); + REQUIRE(*x2 == 2); + REQUIRE(*v2 == 2); +} + +struct Foo; + +struct Test : hilti::rt::Controllable { + std::optional> f; +}; + +struct Foo : hilti::rt::Controllable { + hilti::rt::WeakReference t; +}; + +TEST_CASE("cyclic") { + hilti::rt::ValueReference test; + auto __test = hilti::rt::ValueReference::self(&*test); + hilti::rt::ValueReference __foo; + + __foo->t = __test; + test->f = (*__foo); +} diff --git a/hilti/src/rt/tests/result.cc b/hilti/src/rt/tests/result.cc new file mode 100644 index 000000000..e025bc333 --- /dev/null +++ b/hilti/src/rt/tests/result.cc @@ -0,0 +1,27 @@ +#include + +#include + +#include + +using namespace hilti::rt; + +TEST_CASE_TEMPLATE("Result", T, Nothing, bool, std::string) { + SUBCASE("default constructed is error") { + Result r; + CHECK(! r); + CHECK(r.errorOrThrow() == result::Error("")); + } + + SUBCASE("conversion to bool") { + Result r; + CHECK(! r); + + if constexpr ( std::is_same_v ) + r = Nothing(); + else + r = T{}; + + CHECK(r); + } +} diff --git a/hilti/src/rt/tests/stream.cc b/hilti/src/rt/tests/stream.cc new file mode 100644 index 000000000..1f6719de3 --- /dev/null +++ b/hilti/src/rt/tests/stream.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::bytes; +using namespace hilti::rt::stream; +using hilti::rt::to_string; + +TEST_CASE("Constructors") { + auto x = Stream("xyz"_b); + CHECK(to_string(x) == R"(b"xyz")"); + CHECK(! x.isEmpty()); + CHECK(x.size().Ref() == 3); + CHECK(x.numberChunks() == 1); + + auto y = Stream("123456789012345678901234567890123"_b); // Exceeds small buffer size. + CHECK(! y.isEmpty()); + CHECK(y.size().Ref() == 33); + CHECK(y.numberChunks() == 1); + CHECK(to_string(y) == R"(b"123456789012345678901234567890123")"); + + auto z = x; + x = Stream(""_b); + CHECK(to_string(z) == R"(b"xyz")"); + CHECK(to_string(x) == R"(b"")"); + CHECK(! z.isEmpty()); + CHECK(z.size().Ref() == 3); + + z = y; + y = Stream(""_b); + CHECK(to_string(z) == R"(b"123456789012345678901234567890123")"); + CHECK(to_string(y) == R"(b"")"); + CHECK(! z.isEmpty()); + CHECK(z.size().Ref() == 33); + + x = Stream("xyz"_b); + z = std::move(x); + CHECK(to_string(z) == R"(b"xyz")"); + CHECK(! z.isEmpty()); + CHECK(z.size().Ref() == 3); + + y = Stream("123456789012345678901234567890123"_b); // Exceeds small buffer size. + z = std::move(y); + CHECK(to_string(z) == R"(b"123456789012345678901234567890123")"); + CHECK(! z.isEmpty()); + CHECK(z.size().Ref() == 33); + + Stream m; + CHECK(to_string(m) == R"(b"")"); + CHECK(m.isEmpty()); + CHECK(m.size().Ref() == 0); + + m = Stream(""_b); + CHECK(to_string(m) == R"(b"")"); + CHECK(m.isEmpty()); + CHECK(m.size().Ref() == 0); + + x = Stream("foo"_b); + CHECK(! x.isFrozen()); + x.freeze(); + CHECK(x.isFrozen()); + + CHECK(Stream("abc"_b) == Stream("abc"_b)); + CHECK(Stream("abc"_b) != Stream("def"_b)); + CHECK(Stream("abc"_b) != Stream(""_b)); +} + +TEST_CASE("Growing") { + // rvalue append + auto x = Stream("1234567890"_b); + CHECK(x.size().Ref() == 10); + CHECK(x.numberChunks() == 1); + + x.append(""_b); + CHECK(to_string(x) == R"(b"1234567890")"); + CHECK(x.size().Ref() == 10); + CHECK(x.numberChunks() == 1); + + x.append("1*3*5*7*9*"_b); + CHECK(to_string(x) == R"(b"12345678901*3*5*7*9*")"); + CHECK(x.size().Ref() == 20); + CHECK(x.numberChunks() == 2); + + x.append("123456789012345"_b); + CHECK(to_string(x) == R"(b"12345678901*3*5*7*9*123456789012345")"); + CHECK(x.size().Ref() == 35); + CHECK(x.numberChunks() == 3); + + // lvalue append + x = Stream("1234567890"_b); + CHECK(x.size().Ref() == 10); + CHECK(x.numberChunks() == 1); + + auto y1 = ""_b; + auto y2 = "1*3*5*7*9*"_b; + auto y3 = "123456789012345"_b; + + x.append(y1); + CHECK(to_string(x) == R"(b"1234567890")"); + CHECK(x.size().Ref() == 10); + CHECK(x.numberChunks() == 1); + + x.append(y2); + CHECK(to_string(x) == R"(b"12345678901*3*5*7*9*")"); + CHECK(x.size().Ref() == 20); + CHECK(x.numberChunks() == 2); + + x.append(y3); + CHECK(to_string(x) == R"(b"12345678901*3*5*7*9*123456789012345")"); + CHECK(x.size().Ref() == 35); + CHECK(x.numberChunks() == 3); +} + +TEST_CASE("Iterators") { + auto x = Stream("12345"_b); + + std::string s; + for ( auto i : x ) + s += i; + + CHECK(s == "12345"); + + x = Stream("12345"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + + s = ""; + for ( auto i : x ) + s += i; + + CHECK(s == "123451234567890123456789012345678901234567890"); + + auto i = x.safeBegin(); + i += 7; + CHECK(*i == '3'); + i += 7; + CHECK(*i == '0'); + i += 1; + CHECK(*i == '1'); + + auto j = x.safeEnd(); + CHECK(j != i); + CHECK(j == x.safeEnd()); + + x.append("abc"_b); + CHECK(j != x.safeEnd()); + CHECK(*j == 'a'); + + ++j; + CHECK(j != x.safeEnd()); + ++j; + CHECK(j != x.safeEnd()); + ++j; + CHECK(j == x.safeEnd()); + + j += 5; + CHECK_THROWS_AS(*j, InvalidIterator); + x.append("1234567890"_b); + CHECK(*j == '6'); + + x = Stream(""_b); + i = x.safeBegin(); + CHECK_THROWS_AS(*i, InvalidIterator); + x.append("1"_b); + CHECK(*i == '1'); + + CHECK_THROWS_AS((void)(*j == '6'), InvalidIterator); // j now invalid. +} + +TEST_CASE("sub") { + auto x = Stream("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + + auto i = (x.safeBegin() + 5); + auto j = (x.safeBegin() + 15); + + CHECK(x.view().sub(i, j) == "6789012345"_b); + + auto y = Stream("12345"_b); + CHECK(y.view().sub(y.safeBegin(), y.safeEnd()) == "12345"_b); + CHECK(y.view().sub(y.safeBegin(), y.safeBegin()) == ""_b); + CHECK(y.view().sub(y.safeEnd(), y.safeEnd()) == ""_b); + + auto f = [](const stream::View& v) { return v.sub(v.safeBegin() + 15, v.safeBegin() + 25); }; + + CHECK(f(x.view()).data() == "6789012345"_b); +} + +TEST_CASE("freezing") { + auto x = Stream("12345"_b); + x.append("123456789A"_b); + x.append("B234567890"_b); + x.append("1234567890"_b); + x.append("123456789D"_b); + x.append("E234567890"_b); + + auto i = (x.safeBegin() + 25); + CHECK(! i.isFrozen()); + x.freeze(); + CHECK(i.isFrozen()); + x.unfreeze(); + CHECK(! i.isFrozen()); +} + +TEST_CASE("convert view to stream") { + auto x = Stream("12345"_b); + auto v = stream::View(x.safeBegin() + 1, x.safeBegin() + 3); + CHECK(v == "23"_b); + auto y = Stream(v); + CHECK(y == "23"_b); + + x.append("ABCDEF"_b); + x.append("GHJI"_b); + v = stream::View(x.safeBegin() + 1, x.safeBegin() + 12); + CHECK(v == "2345ABCDEFG"_b); + y = Stream(v); + CHECK(y == "2345ABCDEFG"_b); +} + +TEST_CASE("Expanding vs non-expanding views") { + auto x = Stream("12345"_b); + auto v1 = x.view(true); // expanding + auto v2 = x.view(false); // non-expanding + x.append("123456789A"_b); + x.append("B234567890"_b); + x.append("1234567890"_b); + x.append("123456789D"_b); + x.append("E234567890"_b); + + CHECK(v1.size().Ref() == 55); + CHECK(v2.size().Ref() == 5); +} + +TEST_CASE("Trim") { + auto x = Stream("12345678901234567890123456789012"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + x.append("1234567890"_b); + + auto y = x; + + CHECK(x.size().Ref() == 72); + CHECK(x.numberChunks() == 5); + + x.trim(x.at(10)); + CHECK(x.size().Ref() == 62); + x.trim(x.at(20)); + CHECK(x.safeBegin().offset().Ref() == 20); + CHECK(x.size().Ref() == 52); + x.trim(x.at(32)); + CHECK(x.size().Ref() == 40); + CHECK(x.numberChunks() == 4); + x.trim(x.at(50)); + CHECK(x.size().Ref() == 22); + CHECK(x.numberChunks() == 3); + x.trim(x.at(65)); + CHECK(x.safeBegin().offset().Ref() == 65); + CHECK(x.size().Ref() == 7); + CHECK(x == "4567890"_b); + CHECK(x.numberChunks() == 1); + x.trim(x.at(72)); + CHECK(x.size().Ref() == 0); + CHECK(x == ""_b); + CHECK(x.numberChunks() == 1); // will stay the same + CHECK(x.safeBegin().offset().Ref() == 72); + + y.trim(y.at(100)); + CHECK(y.size().Ref() == 0); + CHECK(y.safeBegin().offset().Ref() == 100); + + auto z = Stream("12345"_b); + z.trim(z.at(3)); + CHECK(z == "45"_b); + CHECK(z.size().Ref() == 2); + z.trim(z.at(5)); + CHECK(z == ""_b); + CHECK(z.size().Ref() == 0); +} + +TEST_CASE("Trim with existing iterator and append") { + auto x = Stream("01"_b); + auto i = x.safeBegin(); + auto j = x.safeBegin(); + + i += 3; + x.append("2345"_b); + j += 2; + x.trim(j); + + CHECK(*i == '3'); +} + +TEST_CASE("Block iteration") { + auto content = [](auto b, auto s) -> bool { return memcmp(b->start, s, strlen(s)) == 0; }; + + auto x = Stream("01234"_b); + + auto v = x.view(); + auto block = v.firstBlock(); + CHECK(block); + CHECK(content(block, "01234")); + CHECK(block->offset == 0); + CHECK(block->size == 5); + CHECK(block->is_first); + CHECK(block->is_last); + CHECK(! v.nextBlock(block)); + + x.append("567"); + x.append("890"); + x.append("abc"); + x.append("def"); + + v = x.view(); + block = v.firstBlock(); + CHECK(block); + CHECK(content(block, "01234")); + CHECK(block->offset == 0); + CHECK(block->size == 5); + CHECK(block->is_first); + CHECK(! block->is_last); + block = v.nextBlock(block); + CHECK(block); + CHECK(content(block, "567")); + CHECK(block->offset == 5); + CHECK(block->size == 3); + CHECK(! block->is_first); + CHECK(! block->is_last); + block = v.nextBlock(block); + CHECK(block); + CHECK(content(block, "890")); + CHECK(block->offset == 8); + CHECK(block->size == 3); + CHECK(! block->is_first); + CHECK(! block->is_last); + block = v.nextBlock(block); + CHECK(block); + CHECK(content(block, "abc")); + CHECK(block->offset == 11); + CHECK(block->size == 3); + CHECK(! block->is_first); + CHECK(! block->is_last); + block = v.nextBlock(block); + CHECK(block); + CHECK(content(block, "def")); + CHECK(block->offset == 14); + CHECK(block->size == 3); + CHECK(! block->is_first); + CHECK(block->is_last); + CHECK(! v.nextBlock(block)); + + v = v.sub(v.at(6), v.at(13)); + block = v.firstBlock(); + CHECK(block); + CHECK(content(block, "67")); + CHECK(block->offset == 6); + CHECK(block->size == 2); + CHECK(block->is_first); + CHECK(! block->is_last); + block = v.nextBlock(block); + CHECK(block); + CHECK(content(block, "890")); + CHECK(block->offset == 8); + CHECK(block->size == 3); + CHECK(! block->is_first); + CHECK(! block->is_last); + block = v.nextBlock(block); + CHECK(block); + CHECK(content(block, "ab")); + CHECK(block->offset == 11); + CHECK(block->size == 2); + CHECK(! block->is_first); + CHECK(block->is_last); + CHECK(! v.nextBlock(block)); +} diff --git a/hilti/src/rt/types/address.cc b/hilti/src/rt/types/address.cc new file mode 100644 index 000000000..66104b9c1 --- /dev/null +++ b/hilti/src/rt/types/address.cc @@ -0,0 +1,169 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti::rt; + +void Address::_parse(const std::string& addr) { + // We need to guess whether it's a struct in_addr or IPv6 address. If + // there's a colon in there, it's the latter. + if ( addr.find(':') == std::string::npos ) { + struct in_addr v4 {}; + if ( inet_pton(AF_INET, addr.c_str(), &v4) > 0 ) + _init(v4); + else + throw RuntimeError(fmt("cannot parse IPv4 address '%s'", addr)); + } + + else { + struct in6_addr v6 {}; + if ( inet_pton(AF_INET6, addr.c_str(), &v6) > 0 ) + _init(v6); + else + throw RuntimeError(fmt("cannot parse IPv6 address '%s'", addr)); + } +} + +void Address::_init(struct in_addr addr) { + _a1 = 0; + _a2 = integer::ntoh32(addr.s_addr); +} + +void Address::_init(struct in6_addr addr) { + memcpy(&_a1, &addr, 8); + _a1 = integer::ntoh64(_a1); + + memcpy(&_a2, (reinterpret_cast(&addr)) + 8, 8); + _a2 = integer::ntoh64(_a2); +} + +AddressFamily Address::family() const { + return (_a1 == 0 && (_a2 & 0xffffffff00000000) == 0) ? AddressFamily::IPv4 : AddressFamily::IPv6; +} + +Address Address::mask(unsigned int width) const { + if ( width == 0 ) + return Address{0, 0}; + + uint64_t a1; + uint64_t a2; + + if ( width < 64 ) + a1 = _a1 & (0xffffffffffffffffU << (64U - width)); + else + a1 = _a1; + + if ( width > 64 ) + a2 = _a2 & (0xffffffffffffffffU << (128U - width)); + else + a2 = 0; + + return Address{a1, a2}; +} + +std::variant Address::asInAddr() const { + switch ( family() ) { + case AddressFamily::IPv4: return in_addr{integer::hton32(_a2)}; + + case AddressFamily::IPv6: { + struct in6_addr v6 {}; + uint64_t a1 = integer::hton64(_a1); + memcpy(&v6, &a1, 8); + + uint64_t a2 = integer::hton64(_a2); + memcpy((reinterpret_cast(&v6)) + 8, &a2, 8); + + return v6; + } + + case AddressFamily::Undef: { + return in_addr{0}; + } + } + + cannot_be_reached(); +} + +bool Address::operator==(const Address& other) const { return _a1 == other._a1 && _a2 == other._a2; } + +Address::operator std::string() const { + auto in_addr = asInAddr(); + char buffer[INET6_ADDRSTRLEN]; + + if ( auto v4 = std::get_if(&in_addr) ) { + if ( inet_ntop(AF_INET, v4, buffer, INET_ADDRSTRLEN) ) + return buffer; + + return ""; + } + else { + auto v6 = std::get_if(&in_addr); + assert(v6); // no other possibility + + if ( inet_ntop(AF_INET6, v6, buffer, INET6_ADDRSTRLEN) ) + return buffer; + + return ""; + } +} + +template +Result> _unpack(const T& data, AddressFamily family, ByteOrder fmt) { + switch ( family ) { + case AddressFamily::IPv4: { + if ( data.size() < 4 ) + return result::Error("insufficient data to unpack IPv4 address"); + + if ( auto x = integer::unpack(data, fmt) ) + return std::make_tuple(Address(std::get<0>(*x)), std::get<1>(*x)); + else + return x.error(); + } + + case AddressFamily::IPv6: { + if ( data.size() < 16 ) + return result::Error("insufficient data to unpack IPv6 address"); + + const bool nbo = + ! (fmt == ByteOrder::Little || (fmt == ByteOrder::Host && systemByteOrder() == ByteOrder::Little)); + + if ( auto x = integer::unpack(data, fmt) ) { + if ( auto y = integer::unpack(std::get<1>(*x), fmt) ) { + if ( ! nbo ) + return std::make_tuple(Address(std::get<0>(*y), std::get<0>(*x)), std::get<1>(*y)); + else + return std::make_tuple(Address(std::get<0>(*x), std::get<0>(*y)), std::get<1>(*y)); + } + else + return y.error(); + } + else + return x.error(); + } + + case AddressFamily::Undef: throw RuntimeError("undefined address family for unpacking"); + default: cannot_be_reached(); + } +} + +Result> address::unpack(const Bytes& data, AddressFamily family, ByteOrder fmt) { + return _unpack(data, family, fmt); +} + +Result> address::unpack(const stream::View& data, AddressFamily family, + ByteOrder fmt) { + return _unpack(data, family, fmt); +} + +std::string detail::adl::to_string(const AddressFamily& x, tag /*unused*/) { + switch ( x ) { + case AddressFamily::IPv4: return "AddressFamily::IPv4"; + case AddressFamily::IPv6: return "AddressFamily::IPv6"; + case AddressFamily::Undef: return "AddressFamily::Undef"; + } + + cannot_be_reached(); +} diff --git a/hilti/src/rt/types/bytes.cc b/hilti/src/rt/types/bytes.cc new file mode 100644 index 000000000..fda5f08ce --- /dev/null +++ b/hilti/src/rt/types/bytes.cc @@ -0,0 +1,167 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::bytes; + +std::tuple Bytes::find(const Bytes& v, const Iterator& n) const { + if ( v.isEmpty() ) + return std::make_tuple(true, n ? n : safeBegin()); + + auto first = *v.safeBegin(); + + for ( auto i = Iterator(n ? n : safeBegin()); true; ++i ) { + if ( i == safeEnd() ) + return std::make_tuple(false, i); + + if ( *i != first ) + continue; + + auto x = i; + auto y = v.safeBegin(); + + for ( ;; ) { + if ( x == safeEnd() ) + return std::make_tuple(false, i); + + if ( *x++ != *y++ ) + break; + + if ( y == v.safeEnd() ) + return std::make_tuple(true, i); + } + } +} + +Bytes::Bytes(std::string s, bytes::Charset cs) { + switch ( cs ) { + case bytes::Charset::UTF8: + // Data is already in UTF-8, just need to copy it over. + *this = std::move(s); + return; + + case bytes::Charset::ASCII: { + // Convert all bytes to 7-bit codepoints. + std::string s; + for ( auto c : *this ) + s += (c >= 32 && c < 0x7f) ? static_cast(c) : '?'; + + *this = std::move(s); + return; + } + + case bytes::Charset::Undef: throw RuntimeError("unknown character set for encoding"); + } + + cannot_be_reached(); +} + +std::string Bytes::decode(bytes::Charset cs) const { + switch ( cs ) { + case bytes::Charset::UTF8: + // Data is already in UTF-8, just need to copy it into a string. + return str(); + + case bytes::Charset::ASCII: { + // Convert non-printable to the unicode replacement character. + std::string s; + for ( auto c : *this ) { + if ( c >= 32 && c < 0x7f ) + s += static_cast(c); + else + s += "\ufffd"; + } + + return s; + } + + case bytes::Charset::Undef: throw RuntimeError("unknown character set for decoding"); + } + + cannot_be_reached(); +} + +Bytes Bytes::strip(const Bytes& set, bytes::Side side) const { + switch ( side ) { + case bytes::Side::Left: return Bytes(hilti::rt::ltrim(*this, set.str())); + + case bytes::Side::Right: return Bytes(hilti::rt::rtrim(*this, set.str())); + + case bytes::Side::Both: return Bytes(hilti::rt::trim(*this, set.str())); + } + + cannot_be_reached(); +} + +Bytes Bytes::strip(bytes::Side side) const { + switch ( side ) { + case bytes::Side::Left: return Bytes(hilti::rt::ltrim(*this)); + + case bytes::Side::Right: return Bytes(hilti::rt::rtrim(*this)); + + case bytes::Side::Both: return Bytes(hilti::rt::trim(*this)); + } + + cannot_be_reached(); +} + +int64_t Bytes::toInt(uint64_t base) const { + int64_t x; + if ( hilti::rt::atoi_n(begin(), end(), base, &x) == end() ) + return x; + + throw RuntimeError("cannot parse bytes as signed integer"); +} + +uint64_t Bytes::toUInt(uint64_t base) const { + int64_t x; + if ( hilti::rt::atoi_n(begin(), end(), base, &x) == end() ) + return x; + + throw RuntimeError("cannot parse bytes as unsigned integer"); +} + +int64_t Bytes::toInt(ByteOrder byte_order) const { + auto i = toUInt(byte_order); + auto size_ = static_cast(size()); + + if ( i & (1U << (size_ * 8 - 1)) ) { + if ( size() == 8 ) + return -(~i + 1); + + return -(i ^ ((1U << (size_ * 8)) - 1)) - 1; + } + + return static_cast(i); +} + +uint64_t Bytes::toUInt(ByteOrder byte_order) const { + if ( byte_order == hilti::rt::ByteOrder::Host ) + return toInt(systemByteOrder()); + + if ( size() > 8 ) + throw RuntimeError("more than max of 8 bytes for conversion to integer"); + + uint64_t i = 0; + + for ( char c : *this ) + i = (i << 8U) | static_cast(c); + + if ( byte_order == hilti::rt::ByteOrder::Little ) + i = integer::flip(i, size()); + + return i; +} + +Result Bytes::match(const RegExp& re, unsigned int group) { + auto groups = re.findGroups(*this); + + if ( groups.empty() ) + return result::Error("not matches found"); + else + return groups.at(group); +} diff --git a/hilti/src/rt/types/integer.cc b/hilti/src/rt/types/integer.cc new file mode 100644 index 000000000..c398d8f0f --- /dev/null +++ b/hilti/src/rt/types/integer.cc @@ -0,0 +1,91 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include + +using namespace hilti::rt; + +uint16_t integer::flip16(uint16_t v) { + union { // NOLINT(hicpp-member-init) + uint32_t ui16; + unsigned char c[2]; + } x; + + unsigned char c; + + x.ui16 = v; + c = x.c[0]; + x.c[0] = x.c[1]; + x.c[1] = c; + + return x.ui16; +} + +uint32_t integer::flip32(uint32_t v) { + union { // NOLINT(hicpp-member-init) + uint32_t ui32; + unsigned char c[4]; + } x; + + unsigned char c; + + x.ui32 = v; + c = x.c[0]; + x.c[0] = x.c[3]; + x.c[3] = c; + c = x.c[1]; + x.c[1] = x.c[2]; + x.c[2] = c; + + return x.ui32; +} + +uint64_t integer::flip64(uint64_t v) { + union { //NOLINT(hicpp-member-init) + uint64_t ui64; + unsigned char c[8]; + } x; + + unsigned char c; + + x.ui64 = v; + c = x.c[0]; + x.c[0] = x.c[7]; + x.c[7] = c; + c = x.c[1]; + x.c[1] = x.c[6]; + x.c[6] = c; + c = x.c[2]; + x.c[2] = x.c[5]; + x.c[5] = c; + c = x.c[3]; + x.c[3] = x.c[4]; + x.c[4] = c; + + return x.ui64; +} + +uint64_t integer::hton64(uint64_t v) { +#if ! __BIG_ENDIAN__ + return integer::flip64(v); +#else + return v; +#endif +} + +uint32_t integer::hton32(uint32_t v) { return ntohl(v); } //NOLINT(hicpp-signed-bitwise) + +uint16_t integer::hton16(uint16_t v) { return ntohs(v); } //NOLINT(hicpp-signed-bitwise) + +uint64_t integer::ntoh64(uint64_t v) { +#if ! __BIG_ENDIAN__ + return integer::flip64(v); +#else + return v; +#endif +} + +uint32_t integer::ntoh32(uint32_t v) { return ntohl(v); } //NOLINT(hicpp-signed-bitwise) + +uint16_t integer::ntoh16(uint16_t v) { return ntohs(v); } //NOLINT(hicpp-signed-bitwise) diff --git a/hilti/src/rt/types/port.cc b/hilti/src/rt/types/port.cc new file mode 100644 index 000000000..d1d05f15f --- /dev/null +++ b/hilti/src/rt/types/port.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti::rt; + +void Port::_parse(const std::string& port) { + const char* s = port.c_str(); + const char* t = s; + + while ( *t && isdigit(*t) ) + ++t; + + if ( s == t || ! *t || ! *(t + 1) || *t != '/' ) + throw RuntimeError("cannot parse port specification"); + + if ( strcasecmp(t, "/tcp") == 0 ) + _protocol = Protocol::TCP; + + else if ( strcasecmp(t, "/udp") == 0 ) + _protocol = Protocol::UDP; + + else if ( strcasecmp(t, "/icmp") == 0 ) + _protocol = Protocol::ICMP; + + else + _protocol = Protocol::Undef; + + try { + _port = std::stoi(s); + } catch ( ... ) { + throw RuntimeError("cannot parse port specification"); + } +} + +Port::operator std::string() const { + std::string protocol; + + switch ( _protocol ) { + case Protocol::ICMP: { + protocol = "icmp"; + break; + } + case Protocol::TCP: { + protocol = "tcp"; + break; + } + case Protocol::UDP: { + protocol = "udp"; + break; + } + case Protocol::Undef: { + protocol = ""; + break; + } + } + + return fmt("%u/%s", _port, protocol); +} + +std::string hilti::rt::detail::adl::to_string(const Protocol& x, adl::tag /*unused*/) { + switch ( x ) { + case Protocol::ICMP: return "Protocol::ICMP"; + case Protocol::TCP: return "Protocol::TCP"; + case Protocol::UDP: return "Protocol::UDP"; + case Protocol::Undef: return ""; + } + + cannot_be_reached(); +} diff --git a/hilti/src/rt/types/real.cc b/hilti/src/rt/types/real.cc new file mode 100644 index 000000000..4a9dbb186 --- /dev/null +++ b/hilti/src/rt/types/real.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace hilti::rt; + +template +Result> _unpack(const T& data, real::Type type, ByteOrder fmt) { + switch ( type ) { + case real::Type::IEEE754_Single: { + if ( data.size() < 4 ) + return result::Error("insufficient data to unpack single precision real"); + + if ( auto x = integer::unpack(data, fmt) ) { + auto d = reinterpret_cast(&std::get<0>(*x)); + return std::make_tuple(static_cast(*d), std::get<1>(*x)); + } + else + return x.error(); + } + + case real::Type::IEEE754_Double: { + if ( auto x = integer::unpack(data, fmt) ) { + auto d = reinterpret_cast(&std::get<0>(*x)); + return std::make_tuple(*d, std::get<1>(*x)); + } + else + return x.error(); + } + + case real::Type::Undef: throw RuntimeError("undefined real type for unpacking"); + } + + cannot_be_reached(); +} + +Result> real::unpack(const Bytes& data, real::Type type, ByteOrder fmt) { + return _unpack(data, type, fmt); +} + +Result> real::unpack(const stream::View& data, real::Type type, ByteOrder fmt) { + return _unpack(data, type, fmt); +} diff --git a/hilti/src/rt/types/regexp.cc b/hilti/src/rt/types/regexp.cc new file mode 100644 index 000000000..0dfbba2de --- /dev/null +++ b/hilti/src/rt/types/regexp.cc @@ -0,0 +1,548 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// Note: We don't run clang-tidy on this file. The use of the JRX's C +// interface triggers all kinds of warnings. + +extern "C" { +#include +} + +#include +#include + +#include + +using namespace hilti::rt; +using namespace hilti::rt::bytes; + +// #define _DEBUG_MATCHING + +struct regexp::MatchState::Pimpl { + jrx_accept_id _acc = 0; + jrx_assertion _first = JRX_ASSERTION_BOL | JRX_ASSERTION_BOD; + ; + jrx_match_state _ms{}; + std::shared_ptr _jrx; +}; + +regexp::MatchState::MatchState(const RegExp& re) { + _pimpl = std::make_unique(); + _pimpl->_jrx = re._jrxShared(); + jrx_match_state_init(_pimpl->_jrx.get(), 0, &_pimpl->_ms); +} + +regexp::MatchState::MatchState(const MatchState& other) { + if ( this == &other ) + return; + + _pimpl = std::make_unique(); + _pimpl->_acc = other._pimpl->_acc; + _pimpl->_first = other._pimpl->_first; + _pimpl->_jrx = other._pimpl->_jrx; + jrx_match_state_copy(&other._pimpl->_ms, &_pimpl->_ms); +} + +regexp::MatchState& regexp::MatchState::operator=(const MatchState& other) { + if ( this == &other ) + return *this; + + if ( _pimpl ) + jrx_match_state_done(&_pimpl->_ms); + + _pimpl = std::make_unique(); + _pimpl->_acc = other._pimpl->_acc; + _pimpl->_first = other._pimpl->_first; + _pimpl->_jrx = other._pimpl->_jrx; + jrx_match_state_copy(&other._pimpl->_ms, &_pimpl->_ms); + return *this; +} + +regexp::MatchState::MatchState() noexcept = default; +regexp::MatchState& regexp::MatchState::operator=(MatchState&&) noexcept = default; +regexp::MatchState::MatchState(MatchState&&) noexcept = default; + +regexp::MatchState::~MatchState() { + if ( _pimpl ) + jrx_match_state_done(&_pimpl->_ms); +} + +std::tuple regexp::MatchState::advance(const stream::View& data) { + if ( ! _pimpl ) + throw PatternError("no regular expression associated with match state"); + + if ( ! _pimpl->_jrx ) + throw MatchStateReuse("matching already complete"); + + auto [rc, offset] = _advance(data, data.isFrozen()); + + if ( rc >= 0 ) { + _pimpl->_jrx = nullptr; + return std::make_tuple(rc, data.trim(data.safeBegin() + offset)); + } + + return std::make_tuple(rc, data.trim(data.safeBegin() + data.size())); +} + +std::tuple regexp::MatchState::advance(const Bytes& data, bool is_final) { + if ( ! _pimpl ) + throw PatternError("no regular expression associated with match state"); + + if ( ! _pimpl->_jrx ) + throw MatchStateReuse("matching already complete"); + + auto [rc, offset] = _advance(Stream(data).view(), is_final); + + if ( rc >= 0 ) { + _pimpl->_jrx = nullptr; + return std::make_tuple(rc, offset); + } + + return std::make_tuple(rc, data.size()); +} + +std::pair regexp::MatchState::_advance(const stream::View& data, bool is_final) { + jrx_assertion first = _pimpl->_first; + jrx_assertion last = 0; + + if ( data.size() ) + _pimpl->_first = 0; + + _pimpl->_ms.offset = 1; // See below why 1. + + if ( data.isEmpty() ) { + if ( is_final && _pimpl->_acc <= 0 ) + _pimpl->_acc = jrx_current_accept(&_pimpl->_ms); + + return std::make_pair(is_final ? _pimpl->_acc : -1, 0); + } + + stream::detail::UnsafeConstIterator cur(data.safeBegin()); + cur += 0; // this will normalize the internal chunk + jrx_accept_id rc = 0; + + // We iterate over raw arrays of continous memory underlying the + // stream data, using the internal API. + for ( auto chunk = cur.chunk(); chunk; chunk = chunk->next().get() ) { + if ( is_final && chunk->isLast() ) + last |= (JRX_ASSERTION_EOL | JRX_ASSERTION_EOD); + + auto block_start = (chunk == cur.chunk() ? chunk->data(data.safeBegin().offset()) : chunk->begin()); + auto block_end = chunk->end(); + auto block_len = (block_end - block_start); + +#ifdef _DEBUG_MATCHING + std::cerr << fmt("feeding |%s| data.offset=%lu\n", + escapeBytes(std::string_view((const char*)block_start, block_len)), data.safeBegin().offset()); +#endif + + rc = jrx_regexec_partial(_pimpl->_jrx.get(), reinterpret_cast(block_start), block_len, first, last, + &_pimpl->_ms, is_final); + + // FIXME: The jrx match_state intializes the offset with 1. Not sure + // why right now but changing that would probably break other things + // we adjust that here for the calculation. + uint64_t view_offset = chunk->offset() + _pimpl->_ms.offset - 1 - cur.chunk()->offset(); + +#ifdef _DEBUG_MATCHING + std::cerr << fmt("-> state=%p rc=%d ms->offset=%d\n", this, rc, _pimpl->_ms.offset); +#endif + + if ( rc == 0 ) + // No further match possible. + return std::make_pair(_pimpl->_acc > 0 ? _pimpl->_acc : 0, view_offset); + + if ( rc > 0 ) { + // Match found. However, we need to wait for more data that could + // potentially be included into the match before returning it. + if ( ! is_final && jrx_can_transition(&_pimpl->_ms) ) + return std::make_pair(-1, 0); + + _pimpl->_acc = rc; + return std::make_pair(_pimpl->_acc, view_offset); + } + }; + + if ( rc < 0 && _pimpl->_acc == 0 ) + // At least one could match with more data. + _pimpl->_acc = -1; + + return std::make_pair(_pimpl->_acc, 0); +} + +RegExp::RegExp(std::string pattern, regexp::Flags flags) : _flags(flags) { + _newJrx(); + _compileOne(std::move(pattern), 0); + jrx_regset_finalize(_jrx()); +} + + +RegExp::RegExp(const std::vector& patterns, regexp::Flags flags) : _flags(flags) { + if ( patterns.empty() ) + throw regexp::PatternError("trying to compile empty pattern set"); + + _flags.no_sub = true; + _newJrx(); + + int idx = 0; + for ( const auto& p : patterns ) + _compileOne(p, idx++); + + jrx_regset_finalize(_jrx()); +} + +void RegExp::_newJrx() { + if ( _jrx_shared ) + throw regexp::PatternError("regexp already compiled"); + + int cflags = (REG_EXTENDED | REG_LAZY); // | REG_DEBUG; + + if ( _flags.no_sub ) + cflags |= (REG_NOSUB | REG_ANCHOR); + + _patterns.clear(); + _jrx_shared = std::shared_ptr(new jrx_regex_t, [=](auto j) { + jrx_regfree(j); + delete j; + }); + jrx_regset_init(_jrx(), -1, cflags); +} + +void RegExp::_compileOne(std::string pattern, int idx) { + if ( auto rc = jrx_regset_add(_jrx(), pattern.c_str(), pattern.size()); rc != REG_OK ) { + static char err[256]; + jrx_regerror(rc, _jrx(), err, sizeof(err)); + throw regexp::PatternError(fmt("error compiling pattern '%s': %s", pattern, err)); + } + + _patterns.push_back(std::move(pattern)); +} + +int32_t RegExp::find(const Bytes& data) const { + if ( ! _jrx() ) + throw regexp::PatternError("regexp not compiled"); + + jrx_match_state ms; + jrx_accept_id acc = _search_pattern(&ms, data, nullptr, nullptr, false, true); + jrx_match_state_done(&ms); + return acc; +} + +static Bytes _subslice(const Bytes& data, jrx_offset so, jrx_offset eo) { + return Bytes(data.sub(data.safeBegin() + so, data.safeBegin() + eo)); +} + +std::tuple RegExp::findSpan(const Bytes& data) const { + if ( ! _jrx() ) + throw regexp::PatternError("regexp not compiled"); + + jrx_offset so = -1; + jrx_offset eo = -1; + jrx_match_state ms; + auto rc = _search_pattern(&ms, data, &so, &eo, false, true); + jrx_match_state_done(&ms); + + if ( rc > 0 ) + return std::make_tuple(rc, _subslice(data, so, eo)); + + + return std::make_tuple(rc, ""_b); +} + +Vector RegExp::findGroups(const Bytes& data) const { + if ( ! _jrx() ) + throw regexp::PatternError("regexp not compiled"); + + if ( _patterns.size() > 1 ) + throw regexp::NotSupported("cannot capture groups during set matching"); + + jrx_offset so = -1; + jrx_offset eo = -1; + jrx_match_state ms; + auto rc = _search_pattern(&ms, data, &so, &eo, false, true); + + Vector groups; + + if ( rc > 0 ) { + groups.emplace_back(_subslice(data, so, eo)); + + if ( auto num_groups = jrx_num_groups(_jrx()); num_groups > 1 ) { + jrx_regmatch_t pmatch[num_groups]; + jrx_reggroups(_jrx(), &ms, num_groups, pmatch); + + for ( int i = 1; i < num_groups; i++ ) { + if ( pmatch[i].rm_so >= 0 ) + groups.emplace_back(_subslice(data, pmatch[i].rm_so, pmatch[i].rm_eo)); + } + } + } + + jrx_match_state_done(&ms); + return groups; +} + +regexp::MatchState RegExp::tokenMatcher() const { return regexp::MatchState(*this); } + +// TODO: This is stripped down version of the previous view-based matchig +// code (see below for original code). Not sure if we still need all of this, +// or could just call jrx-* functions directly instead of _search_pattern. +jrx_accept_id RegExp::_search_pattern(jrx_match_state* ms, const Bytes& data, jrx_offset* so, jrx_offset* eo, + bool do_anchor, bool find_partial_matches) const { + assert((! do_anchor) || _flags.no_sub); + const auto use_stdmatcher = ! _flags.no_sub; + const jrx_assertion last = JRX_ASSERTION_EOL | JRX_ASSERTION_EOD; + jrx_assertion first = JRX_ASSERTION_BOL | JRX_ASSERTION_BOD; + + jrx_accept_id acc = 0; + int8_t need_msdone = 0; + + if ( data.isEmpty() ) { + // Nothing to do, but still need to init the match state. + jrx_match_state_init(_jrx(), 0, ms); + return -1; + } + + jrx_offset cur = 0; + + while ( acc <= 0 && cur < data.size() ) { + if ( need_msdone ) { + jrx_match_state_done(ms); + + if ( jrx_is_anchored(_jrx()) ) + return 0; + + first = 0; + } + + need_msdone = 1; + + jrx_match_state_init(_jrx(), cur, ms); + + auto block_start = data.data() + cur; + auto block_len = data.size() - cur; + +#ifdef _DEBUG_MATCHING + std::cerr << fmt("feeding |%s| use_stdmatcher=%u do_anchor=%u first=%u last=%u\n", + escapeBytes(std::string_view((const char*)block_start, block_len)), use_stdmatcher, do_anchor, + first, last); +#endif + auto rc = jrx_regexec_partial(_jrx(), reinterpret_cast(block_start), block_len, first, last, ms, + find_partial_matches); + +#ifdef _DEBUG_MATCHING + std::cerr << fmt("-> rc=%d ms->offset=%d\n", rc, ms->offset); +#endif + if ( use_stdmatcher && rc == 0 ) + // No further match. + return acc; + + if ( rc > 0 ) { + // Match. + acc = rc; +#ifdef _DEBUG_MATCHING + std::cerr << fmt("offset=%ld ms->offset=%d eo=%p so=%p\n", cur, ms->offset - 1, eo, so); +#endif + + if ( ! use_stdmatcher ) { + if ( so ) + *so = cur; + + if ( eo ) + // FIXME: The match_state intializes the offset with + // 1. Not sure why right now but changing that would + // probably break other things we adjust that here + // for the calculation. + *eo = cur + ms->offset - 1; + } + else if ( so || eo ) { + jrx_regmatch_t pmatch; + jrx_reggroups(_jrx(), ms, 1, &pmatch); + + if ( so ) + *so = pmatch.rm_so; + + if ( eo ) + *eo = pmatch.rm_eo; + } + + return acc; + } + + if ( rc < 0 && acc == 0 ) + // At least one could match with more data. + acc = -1; + + if ( use_stdmatcher || do_anchor ) + // We compiled with an implicit ".*", or are asked to anchor. + break; + + ++cur; + } + + if ( ! use_stdmatcher && acc == 0 ) + // Adding more data may always help. + return -1; + + return acc; +} + +#if 0 +// Searches for the regexp anywhere inside a bytes view and returns the first +// match. +// +// Note: This is the streaming version which we don't need for bytes anymore, +// but could bring back for streans. + +/* +jrx_accept_id RegExp::_search_pattern(jrx_match_state* ms, const bytes::View& data, jrx_offset* so, jrx_offset* eo, + bool do_anchor, bool find_partial_matches) const { + // We follow one of two strategies here: + // + // (1) If the compilation was compiled with the ability to capture + // subgroups, we have to use the more expensive standard matcher anyway. + // In that case, the compilation didn't anchor the regexp (i.e,, it will + // start with an implicit ".*") and we just need a single matching + // process over all the data. + // + // (2) If we compiled with REG_NOSUB, we iterate ourselves over all + // possible starting positions so that even though using the more + // efficinet minimal matcher, we can still get starting and end + // positions. (Setting do_anchor to 1 prevents the iteration and will + // only match right from the beginning. Note that this flag only works + // with REG_NOSUB). + // + // If find_partial_matches is 0, we don't report a match as long as more + // input could still change the result (i.e., there are still DFA + // transitions possible after processing the last bytes). In this case, + // the function returns -1 as if there wasn't any match yet. + // + // FIXME: In (2), we might be doing a bit more comparisions than with an + // implicit .*, and the manual loop also adds a bit overhead. That seems + // worth it but should reevaluate the trade-off later. + + jrx_assertion first = JRX_ASSERTION_BOL | JRX_ASSERTION_BOD; + jrx_assertion last = 0; + jrx_accept_id acc = 0; + int8_t need_msdone = 0; + bytes::Offset offset = 0; + int bytes_seen = 0; + + assert( (! do_anchor) || _flags.no_sub ); + const auto use_stdmatcher = ! _flags.no_sub; + + if ( data.isEmpty() ) { + // Nothing to do, but still need to init the match state. + jrx_match_state_init(_jrx(), offset, ms); + return -1; + } + + Iterator cur(data.safeBegin()); + + while ( acc <= 0 && cur != data.safeEnd() ) { + if ( need_msdone ) + jrx_match_state_done(ms); + + need_msdone = 1; + bytes_seen = 0; + + jrx_match_state_init(_jrx(), offset, ms); + + // We iterate over raw arrays of continous memory underlying the + // bytes data, using the internal API. + for ( auto chunk = cur.chunk(); chunk; chunk = chunk->next().get() ) { + if ( chunk->isLast() ) + last |= JRX_ASSERTION_EOL | JRX_ASSERTION_EOD; + + auto block_start = chunk->data(cur.offset()); + auto block_end = chunk->end(); + auto block_len = (block_end - block_start); + auto fpm = chunk->isLast() && find_partial_matches; + +#ifdef _DEBUG_MATCHING + std::cerr << fmt("feeding |%s| use_stdmatcher=%u do_anchor=%u first=%u last=%u\n", + escapeBytes(std::string_view((const char*)block_start, block_len)), use_stdmatcher, + do_anchor, first, last); +#endif + auto rc = jrx_regexec_partial(_jrx(), reinterpret_cast(block_start), block_len, first, last, + ms, fpm); + +#ifdef _DEBUG_MATCHING + std::cerr << fmt("-> rc=%d ms->offset=%d\n", rc, ms->offset); +#endif + if ( use_stdmatcher && rc == 0 ) + // No further match. + return acc; + + if ( rc > 0 ) { + // Match. + acc = rc; +#ifdef _DEBUG_MATCHING + std::cerr << fmt("offset=%ld ms->offset=%d bytes_seen=%d eo=%p so=%p\n", offset, ms->offset - 1, + bytes_seen, eo, so); +#endif + + if ( ! use_stdmatcher ) { + if ( so ) + *so = offset; + + if ( eo ) + // FIXME: The match_state intializes the offset with + // 1. Not sure why right now but changing that would + // probably break other things we adjust that here + // for the calculation. + *eo = offset + ms->offset - 1; + } + else if ( so || eo ) { + jrx_regmatch_t pmatch; + jrx_reggroups(_jrx(), ms, 1, &pmatch); + + if ( so ) + *so = pmatch.rm_so; + + if ( eo ) + *eo = pmatch.rm_eo; + } + + return acc; + } + + bytes_seen += block_len; + + if ( chunk->isLast() && rc < 0 && acc == 0 ) + // At least one could match with more data. + acc = -1; + } + + if ( use_stdmatcher || do_anchor ) + // We compiled with an implicit ".*", or are asked to anchor. + break; + + ++cur; + ++offset; + first = 0; + } + + if ( ! use_stdmatcher && acc == 0 ) + // Adding more data may always help. + return -1; + + return acc; +} +*/ +#endif + +std::string hilti::rt::detail::adl::to_string(const RegExp& x, adl::tag /*unused*/) { + if ( x.patterns().empty() ) + return ""; + + auto p = join(transform(x.patterns(), [&](auto s) { return fmt("/%s/", s); }), " | "); + + auto f = std::vector(); + + if ( x.flags().no_sub ) + f.emplace_back("&nosub"); + + if ( f.empty() ) + return p; + + return fmt("%s %s", p, join(f, " ")); +} diff --git a/hilti/src/rt/types/stream.cc b/hilti/src/rt/types/stream.cc new file mode 100644 index 000000000..0cf5a53c2 --- /dev/null +++ b/hilti/src/rt/types/stream.cc @@ -0,0 +1,501 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti::rt; +using namespace hilti::rt::stream; +using namespace hilti::rt::stream::detail; + +Chunk::Chunk(const View& d) : _offset(0) { + if ( d.size() <= SmallBufferSize ) { + std::array a{}; + d.copyRaw(a.data()); + _data = std::make_pair(d.size(), a); + } + else { + std::vector v; + v.resize(d.size()); + d.copyRaw(v.data()); + _data = std::move(v); + } +} + +Chunk::Chunk(const std::string& s) : _offset(0) { + if ( s.size() <= SmallBufferSize ) { + std::array a{}; + memcpy(a.data(), s.data(), s.size()); + _data = std::make_pair(s.size(), a); + } + else { + std::vector v; + v.resize(s.size()); + memcpy(v.data(), s.data(), s.size()); + _data = std::move(v); + } +} + +bool Chunk::tryAppend(const Chunk& d) { + if ( ! isCompact() ) + return false; + + auto& a = std::get(_data); + + auto nsize = a.first + d.size(); + + if ( nsize > SmallBufferSize ) + return false; + + memcpy(a.second.data() + a.first.Ref(), d.begin(), d.size()); + a.first = nsize; + return true; +} + +void Chunk::trim(Offset o) { + assert(o >= _offset && o < _offset + size()); + if ( auto a = std::get_if(&_data) ) { + auto begin = a->second.data() + (o - _offset).Ref(); + auto end = a->second.data() + a->first.Ref(); + a->first = (end - begin); + memmove(a->second.data(), begin, a->first.Ref()); + } + else { + auto& v = std::get(_data); + v.erase(v.begin(), v.begin() + (o - _offset).Ref()); + } + + _offset = o; +} + +SafeConstIterator View::find(Byte b, const SafeConstIterator& n) const { + for ( auto i = UnsafeConstIterator(n ? n : _begin); i != UnsafeConstIterator(safeEnd()); ++i ) { + if ( *i == b ) + return SafeConstIterator(i); + } + + return safeEnd(); +} + +std::tuple View::find(const View& v, const SafeConstIterator& n) const { + if ( v.isEmpty() ) + return std::make_tuple(true, n ? n : _begin); + + auto first = *v.safeBegin(); + + for ( auto i = UnsafeConstIterator(n ? n : _begin); true; ++i ) { + if ( i == UnsafeConstIterator(safeEnd()) ) + return std::make_tuple(false, SafeConstIterator(i)); + + if ( *i != first ) + continue; + + auto x = i; + auto y = UnsafeConstIterator(v._begin); + + for ( ;; ) { + if ( x == UnsafeConstIterator(safeEnd()) ) + return std::make_tuple(false, SafeConstIterator(i)); + + if ( *x++ != *y++ ) + break; + + if ( y == UnsafeConstIterator(v.safeEnd()) ) + return std::make_tuple(true, SafeConstIterator(i)); + } + } +} + +std::tuple View::find(const Bytes& v, const SafeConstIterator& n) const { + if ( v.isEmpty() ) + return std::make_tuple(true, n ? n : _begin); + + auto first = *v.safeBegin(); + + for ( auto i = UnsafeConstIterator(n ? n : _begin); true; ++i ) { + if ( i == UnsafeConstIterator(safeEnd()) ) + return std::make_tuple(false, SafeConstIterator(i)); + + if ( *i != first ) + continue; + + auto x = i; + auto y = v.begin(); + + for ( ;; ) { + if ( x == UnsafeConstIterator(safeEnd()) ) + return std::make_tuple(false, SafeConstIterator(i)); + + if ( *x++ != *y++ ) + break; + + if ( y == v.end() ) + return std::make_tuple(true, SafeConstIterator(i)); + } + } +} + +bool View::startsWith(const Bytes& b) const { + auto s1 = begin(); + auto e1 = end(); + auto s2 = b.begin(); + auto e2 = b.end(); + + while ( s1 != e1 && s2 != e2 ) { + if ( *s1++ != *s2++ ) + return false; + } + + return s2 == e2; +} + +void View::copyRaw(Byte* dst) const { + for ( auto i = begin(); i != end(); ++i ) + *dst++ = *i; +} + +std::optional View::firstBlock() const { + if ( begin() == end() ) + return {}; + + auto chunk = begin().chunk(); + bool is_last = chunk->isLast(); + + if ( _end && _end->offset() <= chunk->offset() + chunk->size() ) + is_last = true; + + return View::Block{.start = chunk->begin() + (_begin.offset() - chunk->offset()).Ref(), + .size = chunk->size() - (_begin.offset() - chunk->offset()), + .offset = _begin.offset(), + .is_first = true, + .is_last = is_last, + ._block = (is_last ? nullptr : chunk)}; +} + +std::optional View::nextBlock(std::optional current) const { + if ( ! (current && current->_block) ) + return {}; + + const Chunk* chunk = current->_block->next().get(); + auto chunk_start_offset = chunk->offset(); + auto chunk_end_offset = chunk->offset() + chunk->size(); + + bool is_last = false; + + if ( end().offset() >= chunk_start_offset && end().offset() <= chunk_end_offset ) + is_last = true; + + if ( is_last ) { + uint64_t size; + + if ( end().offset() < chunk_end_offset ) + size = (end().offset() - chunk_start_offset); + else + size = chunk->size(); + + return View::Block{.start = chunk->begin(), + .size = size, + .offset = chunk->offset(), + .is_first = false, + .is_last = true, + ._block = nullptr}; + } + else { + return View::Block{.start = chunk->begin(), + .size = chunk->size(), + .offset = chunk->offset(), + .is_first = false, + .is_last = false, + ._block = chunk}; + } +} + +Stream::Stream(const Bytes& d) : Stream(Chunk(d.str())) {} + +int Stream::numberChunks() const { + int n = 0; + for ( auto ch = _content->head; ch; ch = ch->next() ) + ++n; + + return n; +} + +void Stream::appendContent(Content&& ocontent) { + auto& ch = _content; + auto& och = ocontent; + + size_t offset = end().offset(); + + for ( auto x = och->head; x; x = x->next() ) + x->setOffset(x->offset() + offset); + + ch->tail->setNext(std::move(och->head)); + ch->tail = std::move(och->tail); +} + +void Stream::append(Bytes&& data) { + if ( data.isEmpty() ) + return; + + if ( _frozen ) + throw Frozen("stream object is frozen"); + + // TODO(robin): Optimize for moce. + appendContent(std::make_shared(data.str())); +} + +void Stream::append(const Bytes& data) { + if ( data.isEmpty() ) + return; + + if ( _frozen ) + throw Frozen("stream object is frozen"); + + appendContent(std::make_shared(data.str())); +} + +void Stream::append(const char* data, size_t len) { + if ( ! len ) + return; + + if ( _frozen ) + throw Frozen("stream object is frozen"); + + appendContent(std::make_shared(std::string(data, len))); +} + +void Stream::trim(const stream::SafeConstIterator& i) { + auto& ch = _content; + + // We search the first chunk that's containing the desired position, deleting + // all the ones we pass on the way. We trim the one that contains the position. + for ( auto c = ch->head; c; c = c->next() ) { + if ( i.offset() >= c->offset() + c->size() ) { + // Delete chunk. + ch->head = c->next(); + if ( c->isLast() ) + ch->tail = c->next(); + + if ( ! ch->head ) { + // Just set the new head to empty chunk. Note that we need to + // keep the current chain object so that iterators don't + // become invalid. + auto chain = _content; + chain->head = chain->tail = std::shared_ptr(new Chunk(i.offset(), {}, 0)); + return; + } + + continue; + } + + if ( c->offset() <= i.offset() && i.offset() < c->offset() + c->size() ) { + c->trim(i.offset()); + break; + } + } +} + +void Stream::freeze() { + _frozen = true; + for ( auto c = head(); c; c = c->next().get() ) + c->freeze(); +} + +void Stream::unfreeze() { + _frozen = false; + for ( auto c = head(); c; c = c->next().get() ) + c->unfreeze(); +} + +int Stream::compare(UnsafeConstIterator s1, const UnsafeConstIterator& e1, UnsafeConstIterator s2, + const UnsafeConstIterator& e2) { + while ( s1 != e1 && s2 != e2 ) { + if ( auto c = (*s1++ - *s2++); c != 0 ) + return c; + } + + if ( s1 != e1 ) + return 1; + + if ( s2 != e2 ) + return -1; + + return 0; +} + +Stream::Content Stream::deepCopyContent() const { + std::shared_ptr head; + std::shared_ptr tail; + + for ( auto ch = _content->head; ch; ch = ch->next() ) { + auto nch = std::make_shared(*ch); + if ( tail ) + tail->setNext(nch); + + if ( ! head ) + head = nch; + + tail = nch; + } + + return std::make_shared(std::move(head), std::move(tail)); +} + +Size View::size() const { + if ( safeEnd().offset() <= _begin.offset() ) + return 0; + + // Not so great: Because our end offset may point beyond what's currently + // available, we actually need to iterate through and count. + // + // TODO(robin): We can build a better loop though. + Size s = 0; + auto x = safeEnd(); + auto end = detail::UnsafeConstIterator(safeEnd()); + for ( auto i = detail::UnsafeConstIterator(_begin); i != end; ++i ) + s++; + + return s; +} + +std::string Stream::data() const { + std::string s; + s.reserve(size()); + + for ( auto i = begin(); i != end(); ++i ) + s += static_cast(*i); + + return s; +} + +std::string stream::View::data() const { + std::string s; + s.reserve(size()); + + for ( auto i = begin(); i != end(); ++i ) + s += static_cast(*i); + + return s; +} + +bool stream::View::operator==(const Stream& other) const { return *this == other.view(); } + +bool stream::View::operator==(const View& other) const { + if ( size() != other.size() ) + return false; + + auto i = begin(); + auto j = other.begin(); + + while ( i != end() ) { + if ( *i++ != *j++ ) + return false; + } + + return true; +} + +bool stream::View::operator==(const Bytes& other) const { + if ( size() != other.size() ) + return false; + + auto i = begin(); + auto j = other.begin(); + + while ( i != end() ) { + if ( *i++ != *j++ ) + return false; + } + + return true; +} + +std::string hilti::rt::detail::adl::to_string(const stream::SafeConstIterator& x, adl::tag /*unused*/) { + auto str = [](auto x) { + auto y = x + 10; + auto v = stream::View(x, y); + if ( y.isEnd() ) + return fmt("%s", v); + + return fmt("%s...", v); + }; + + if ( x.isExpired() ) + return ""; + + if ( x.isUnset() ) + return ""; + + return fmt("", x.offset(), str(x)); +} + +void SafeConstIterator::debugPrint(std::ostream& out) const { + int chunk = 0; + + auto c = _content.lock()->head.get(); + while ( c ) { + if ( c == _chunk.lock().get() ) + break; + + chunk++; + c = c->next().get(); + } + + if ( ! c ) + // Can happen if trimmed off. + chunk = -1; + + out << fmt("iterator %p: parent=%p chunk=#%d offset=%llu is_end=%d\n", this, _content.lock().get(), chunk, _offset, + static_cast(isEnd())); +} + +void UnsafeConstIterator::debugPrint(std::ostream& out) const { + int chunk = 0; + + auto c = _content.lock()->head.get(); + while ( c ) { + if ( c == _chunk ) + break; + + chunk++; + c = c->next().get(); + } + + if ( ! c ) + // Can happen if trimmed off. + chunk = -1; + + out << fmt("iterator %p: parent=%p chunk=#%d offset=%llu is_end=%d\n", this, _content.lock().get(), chunk, _offset, + static_cast(isEnd())); +} + +void View::debugPrint(std::ostream& out) const { + out << "[begin] "; + _begin.debugPrint(out); + + out << "[end] "; + + if ( _end ) + _end->debugPrint(out); + else + out << ""; + + out << "[data]" << std::endl; + Stream::debugPrint(out, _begin._content.lock().get()); +} + +void Stream::debugPrint(std::ostream& out, const stream::detail::Chain* chain) { + out << fmt("chain %p", chain) << std::endl; + int i = 0; + for ( auto c = chain->head.get(); c; c = c->next().get() ) { + out << fmt(" #%d: ", i++); + c->debugPrint(out); + } +} + +void Stream::debugPrint(std::ostream& out) const { debugPrint(out, _content.get()); } + +void Chunk::debugPrint(std::ostream& out) const { + auto x = std::string(reinterpret_cast(begin()), size()); + x = escapeBytes(x); + out << fmt("offset %lu frozen=%s data=|%s|", _offset, (_frozen ? "yes" : "no"), x) << std::endl; +} diff --git a/hilti/src/rt/types/string.cc b/hilti/src/rt/types/string.cc new file mode 100644 index 000000000..665811285 --- /dev/null +++ b/hilti/src/rt/types/string.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace hilti::rt; + +size_t string::size(const std::string& s) { + auto p = reinterpret_cast(s.data()); + auto e = p + s.size(); + + size_t len = 0; + + while ( p < e ) { + utf8proc_int32_t cp; + auto n = utf8proc_iterate(p, e - p, &cp); + + if ( n < 0 ) + internalError("illegal UTF8 sequence in string"); + + ++len; + p += n; + } + + return len; +} + +std::string string::upper(const std::string& s) { + auto p = reinterpret_cast(s.data()); + auto e = p + s.size(); + + unsigned char buf[4]; + std::string rval; + + while ( p < e ) { + utf8proc_int32_t cp; + auto n = utf8proc_iterate(p, e - p, &cp); + + if ( n < 0 ) + internalError("illegal UTF8 sequence in string"); + + auto m = utf8proc_encode_char(utf8proc_toupper(cp), buf); + rval += std::string(reinterpret_cast(buf), m); + p += n; + } + + return rval; +} + +std::string string::lower(const std::string& s) { + auto p = reinterpret_cast(s.data()); + auto e = p + s.size(); + + unsigned char buf[4]; + std::string rval; + + while ( p < e ) { + utf8proc_int32_t cp; + auto n = utf8proc_iterate(p, e - p, &cp); + + if ( n < 0 ) + internalError("illegal UTF8 sequence in string"); + + auto m = utf8proc_encode_char(utf8proc_tolower(cp), buf); + rval += std::string(reinterpret_cast(buf), m); + p += n; + } + + return rval; +} diff --git a/hilti/src/rt/types/time.cc b/hilti/src/rt/types/time.cc new file mode 100644 index 000000000..258ab4ce4 --- /dev/null +++ b/hilti/src/rt/types/time.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +using namespace hilti::rt; + +Time time::current_time() { + struct timeval tv {}; + if ( gettimeofday(&tv, nullptr) < 0 ) + throw RuntimeError("gettimeofday failed in current_time()"); + + double t = double(tv.tv_sec) + double(tv.tv_usec) / 1e6; + return Time(t); +} + +Time::operator std::string() const { + if ( _nsecs == 0 ) + return ""; + + // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) + double frac = (_nsecs % 1000000000) / 1e9; + time_t secs = _nsecs / 1000000000; + + char buffer[60]; + struct tm tm {}; + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", gmtime_r(&secs, &tm)); + auto sfrac = fmt("%.9fZ", frac); + return fmt("%s.%s", buffer, sfrac.substr(2)); +} diff --git a/hilti/src/rt/util.cc b/hilti/src/rt/util.cc new file mode 100644 index 000000000..4b54e2726 --- /dev/null +++ b/hilti/src/rt/util.cc @@ -0,0 +1,316 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include + +#include + +#include + +#include +#include +#include + +std::string hilti::rt::version() { + constexpr char hilti_version[] = PROJECT_VERSION_STRING_LONG; + +#if HILTI_RT_BUILD_TYPE_DEBUG + return hilti::rt::fmt("HILTI runtime library version %s [debug build]", hilti_version); +#elif HILTI_RT_BUILD_TYPE_RELEASE + return hilti::rt::fmt("HILTI runtime library version %s [release build]", hilti_version); +#else +#error "Neither HILTI_RT_BUILD_TYPE_DEBUG nor HILTI_RT_BUILD_TYPE_RELEASE define." +#endif +} + +bool hilti::rt::isDebugVersion() { +#if HILTI_RT_BUILD_TYPE_DEBUG + return true; +#elif HILTI_RT_BUILD_TYPE_RELEASE + return false; +#else +#error "Neither HILTI_RT_BUILD_TYPE_DEBUG nor HILTI_RT_BUILD_TYPE_RELEASE define." +#endif +} + +void hilti::rt::abort_with_backtrace() { + fputs("\n--- Aborting in libhilti\n", stderr); + hilti::rt::Backtrace bt; + for ( const auto& f : bt.backtrace() ) + std::cerr << f << std::endl; + abort(); +} + +void hilti::rt::cannot_be_reached() { hilti::rt::internalError("code is executing that should not be reachable"); } + +hilti::rt::MemoryStatistics hilti::rt::memory_statistics() { + MemoryStatistics stats; + + struct rusage r; + getrusage(RUSAGE_SELF, &r); + auto fibers = detail::Fiber::statistics(); + + stats.memory_heap = r.ru_maxrss * 1024; + stats.num_fibers = fibers.current; + stats.max_fibers = fibers.max; + stats.cached_fibers = fibers.cached; + + return stats; +} + +std::vector hilti::rt::split(std::string_view s, std::string_view delim) { + std::vector l; + + do { + size_t p = s.find(delim); + l.push_back(s.substr(0, p)); + if ( p == std::string_view::npos ) + break; + + s.remove_prefix(p + delim.size()); + } while ( ! s.empty() ); + + return l; +} + +std::vector hilti::rt::split(std::string_view s) { + std::vector l; + + s = trim(s); + + while ( ! s.empty() ) { + size_t p = s.find_first_of(detail::whitespace_chars); + l.push_back(s.substr(0, p)); + if ( p == std::string_view::npos ) + break; + + s.remove_prefix(p + 1); + s = ltrim(s); + } + + return l; +} + +std::pair hilti::rt::split1(std::string s) { + if ( auto i = s.find_first_of(detail::whitespace_chars); i != std::string::npos ) + return std::make_pair(s.substr(0, i), std::string(ltrim(s.substr(i + 1)))); + + return std::make_pair(std::move(s), ""); +} + +std::pair hilti::rt::rsplit1(std::string s) { + if ( auto i = s.find_last_of(detail::whitespace_chars); i != std::string::npos ) + return std::make_pair(s.substr(0, i), std::string(rtrim(s.substr(i + 1)))); + + return std::make_pair("", std::move(s)); +} + +std::pair hilti::rt::split1(std::string s, const std::string& delim) { + if ( auto i = s.find(delim); i != std::string::npos ) + return std::make_pair(s.substr(0, i), s.substr(i + delim.size())); + + return std::make_pair(std::move(s), ""); +} + +std::pair hilti::rt::rsplit1(std::string s, const std::string& delim) { + if ( auto i = s.rfind(delim); i != std::string::npos ) + return std::make_pair(s.substr(0, i), s.substr(i + delim.size())); + + return std::make_pair("", std::move(s)); +} + +// In-place implementation copies chars shrinking escape sequences to binary. +// Requires that binary results are not larger than their escape sequence. +std::string hilti::rt::expandEscapes(std::string s) { + auto d = s.begin(); + for ( auto c = d; c != s.end(); ) { + if ( *c != '\\' ) { + *d++ = *c++; + continue; + } + + ++c; + + if ( c == s.end() ) + throw Exception("broken escape sequence"); + + switch ( *c++ ) { + case '\\': *d++ = '\\'; break; + + case '"': *d++ = '"'; break; + + case 'n': *d++ = '\n'; break; + + case 'r': *d++ = '\r'; break; + + case 't': *d++ = '\t'; break; + + case 'u': { + auto end = c + 4; + if ( end > s.end() ) + throw Exception("incomplete unicode \\u"); + utf8proc_int32_t val; + c = atoi_n(c, end, 16, &val); + + if ( c != end ) + throw Exception("cannot decode character"); + + uint8_t tmp[4]; + int len = utf8proc_encode_char(val, tmp); + + if ( ! len ) + throw Exception("cannot encode unicode code point"); + + d = std::copy(tmp, tmp + len, d); + break; + } + + case 'U': { + auto end = c + 8; + if ( end > s.end() ) + throw Exception("incomplete unicode \\U"); + utf8proc_int32_t val; + c = atoi_n(c, end, 16, &val); + + if ( c != end ) + throw Exception("cannot decode character"); + + uint8_t tmp[4]; + int len = utf8proc_encode_char(val, tmp); + + if ( ! len ) + throw Exception("cannot encode unicode code point"); + + d = std::copy(tmp, tmp + len, d); + break; + } + + case 'x': { + auto end = std::min(c + 2, s.end()); + if ( c == s.end() ) + throw Exception("\\x used with no following hex digits"); + char val; + c = atoi_n(c, end, 16, &val); + + if ( c != end ) + throw Exception("cannot decode character"); + + *d++ = val; + break; + } + + default: throw Exception("unknown escape sequence"); + } + } + + s.resize(d - s.begin()); + return s; +} + +std::string hilti::rt::escapeUTF8(std::string_view s, bool escape_quotes, bool escape_control) { + auto escapeControl = [escape_control](unsigned char c, const char* s) { + return escape_control ? fmt(s) : std::string(1, c); + }; + + auto p = reinterpret_cast(s.data()); + auto e = p + s.size(); + + std::string esc; + + while ( p < e ) { + utf8proc_int32_t cp; + + ssize_t n = utf8proc_iterate(p, e - p, &cp); + + if ( n < 0 ) { + esc += ""; + break; + } + + if ( cp == '\\' ) + esc += "\\\\"; + + else if ( cp == '"' && escape_quotes ) + esc += "\\\""; + + else if ( *p == '\n' ) + esc += escapeControl(*p, "\\n"); + + else if ( *p == '\r' ) + esc += escapeControl(*p, "\\r"); + + else if ( *p == '\t' ) + esc += escapeControl(*p, "\\t"); + + else { + for ( ssize_t i = 0; i < n; i++ ) + esc += static_cast(p[i]); + } + + p += n; + } + + return esc; +} + +std::string hilti::rt::escapeBytes(std::string_view s, bool escape_quotes, bool escape_control) { + auto escapeControl = [escape_control](char c, const char* s) { + return escape_control ? fmt(s) : std::string(1, c); + }; + + auto p = s.data(); + auto e = p + s.size(); + + std::string esc; + + while ( p < e ) { + if ( *p == '\\' ) + esc += "\\\\"; + + else if ( *p == '"' && escape_quotes ) + esc += "\\\""; + + else if ( *p == '\n' ) + esc += escapeControl(*p, "\\n"); + + else if ( *p == '\r' ) + esc += escapeControl(*p, "\\r"); + + else if ( *p == '\t' ) + esc += escapeControl(*p, "\\t"); + + else if ( isprint(*p) ) + esc += *p; + + else + esc += fmt("\\x%02x", static_cast(*p)); + + ++p; + } + + return esc; +} + +std::string hilti::rt::replace(std::string s, std::string_view o, std::string_view n) { + if ( o.empty() ) + return s; + + size_t i = 0; + while ( (i = s.find(o, i)) != std::string::npos ) { + s.replace(i, o.length(), n); + i += n.length(); + } + + return s; +} + +hilti::rt::ByteOrder hilti::rt::systemByteOrder() { +#ifdef LITTLE_ENDIAN + return ByteOrder::Little; +#elif BIG_ENDIAN + return ByteOrder::Big; +#else +#error Neither LITTLE_ENDIAN nor BIG_ENDIAN defined. +#endif +} diff --git a/hilti/tests/main.cc b/hilti/tests/main.cc new file mode 100644 index 000000000..ce23c64db --- /dev/null +++ b/hilti/tests/main.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include diff --git a/hilti/tests/util.cc b/hilti/tests/util.cc new file mode 100644 index 000000000..b3eb502ac --- /dev/null +++ b/hilti/tests/util.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// @TEST-GROUP: no-jit +// @TEST-REQUIRES: using-build-directory +// @TEST-EXEC: test-util >&2 +// +// Note: This is compiled through CMakeLists.txt. + +#include + +#include + +#include + +enum class Foo { AAA, BBB, CCC }; + +constexpr util::enum_::Value values[] = { + {Foo::AAA, "aaa"}, + {Foo::BBB, "bbb"}, + {Foo::CCC, "ccc"}, +}; + +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, values); } +constexpr auto to_string(Foo f) { return util::enum_::to_string(f, values); } + +TEST_CASE("enum string conversion") { + CHECK(from_string("aaa") == Foo::AAA); + CHECK(from_string("ccc") == Foo::CCC); + CHECK_THROWS_AS(from_string("xxx"), std::out_of_range); // NOLINT +} diff --git a/hilti/tests/visitor.cc b/hilti/tests/visitor.cc new file mode 100644 index 000000000..86f709c8e --- /dev/null +++ b/hilti/tests/visitor.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// @TEST-GROUP: no-jit +// @TEST-REQUIRES: using-build-directory +// @TEST-EXEC: test-visitor >&2 +// +// Note: This is compiled through CMakeLists.txt. + +#include + +#include +#include + +#include +#include + +static auto ast() { + auto s = hilti::declaration::Type(hilti::ID("s"), hilti::type::String()); + auto i32 = hilti::declaration::Type(hilti::ID("i32"), hilti::type::SignedInteger(32)); + auto d = hilti::declaration::Type(hilti::ID("d"), hilti::type::Real()); + auto e = hilti::declaration::LocalVariable(hilti::ID("e"), hilti::type::Void()); + auto c = hilti::declaration::LocalVariable(hilti::ID("c"), hilti::type::Bool(), + hilti::expression::Ctor(hilti::ctor::Bool(true))); + + std::vector x = {s, i32, d, e, c}; + auto m = hilti::Module(hilti::ID("test"), std::move(x)); + return hilti::Node{std::move(m)}; +} + +TEST_CASE("Single-shot, result, constant node") { + struct Visitor : hilti::visitor::PreOrder { + using base_t::base_t; + + result_t operator()(const hilti::Module& m) { return "(mo)"; } + result_t operator()(const hilti::ID& id) { return "(id)"; } + result_t operator()(const hilti::Type& t, const_position_t i) { return "(t)"; } + result_t operator()(const hilti::type::String& s) { return "(ts)"; } + result_t operator()(const hilti::type::SignedInteger& i) { return "(ti)"; } + result_t operator()(const hilti::expression::Ctor& c, const_position_t i) { return "(e:c)"; } + result_t operator()(const hilti::ctor::Bool& b) { return "(c:b)"; } + + void testDispatch(iterator_t::Position i) { + if ( auto s = dispatch(i) ) + x += *s; + else + x += "-"; + + x += ","; + } + + std::string x; + const std::string expected = + "(mo),(id),-,-,(id),(ts),-,(id),(ti),-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),"; + }; + + auto root = ast(); + auto v = Visitor(); + + auto x = v.dispatch(root); + REQUIRE(x); + REQUIRE(*x == "(mo)"); + + x = v.dispatch(root.childs()[0]); + REQUIRE(x); + REQUIRE(*x == "(id)"); + + x = v.dispatch(root.childs()[1]); + REQUIRE(! x); +} + +TEST_CASE("Visitor, pre-order, no result, constant nodes") { + struct Visitor : hilti::visitor::PreOrder { + using base_t::base_t; + + result_t operator()(const hilti::Module& m) { x += "(mo)"; } + result_t operator()(const hilti::ID& id) { x += "(id)"; } + result_t operator()(const hilti::Type& t, const_position_t i) { x += "(t)"; } + result_t operator()(const hilti::type::String& s) { x += "(ts)"; } + result_t operator()(const hilti::type::SignedInteger& i) { x += "(ti)"; } + result_t operator()(const hilti::expression::Ctor& c, const_position_t i) { x += "(e:c)"; } + result_t operator()(const hilti::ctor::Bool& b) { x += "(c:b)"; } + + void testDispatch(iterator_t::Position i) { + if ( ! dispatch(i) ) + x += "-"; + x += ","; + } + void testDispatch(const_iterator_t::Position i) { + if ( ! dispatch(i) ) + x += "-"; + x += ","; + } + + std::string x; + const std::string expected = + "(mo),(id),-,-,(id),(ts)(t),-,-,(id),(ti)(t),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),"; + }; + + // Node an rvalue. + auto root0 = ast(); + auto v = Visitor(); + for ( auto i : v.walk(root0) ) + v.testDispatch(i); + + CHECK(v.x == v.expected); + + // Node a lvalue. + auto root1 = ast(); + auto v2 = Visitor(); + for ( auto i : v2.walk(root1) ) + v2.testDispatch(i); + + CHECK(v2.x == v2.expected); + + // Node a const value. + const auto root2 = ast(); + auto v3 = Visitor(); + for ( auto i : v3.walk(root2) ) + v3.testDispatch(i); + + CHECK(v3.x == v3.expected); + + // Visitor an rvalue. + auto root4 = ast(); + int c = 0; + + auto walk = Visitor().walk(root4); + std::for_each(walk.begin(), walk.end(), [&](auto&&) { ++c; }); + + CHECK(c == 24); +} + +TEST_CASE("Visitor, pre-order, with result, constant nodes") { + struct Visitor : hilti::visitor::PreOrder { + using base_t::base_t; + + result_t operator()(const hilti::Module& m) { return "(mo)"; } + result_t operator()(const hilti::ID& id) { return "(id)"; } + result_t operator()(const hilti::Type& t, const_position_t i) { return "(t)"; } + result_t operator()(const hilti::type::String& s) { return "(ts)"; } + result_t operator()(const hilti::type::SignedInteger& i) { return "(ti)"; } + result_t operator()(const hilti::expression::Ctor& c, const_position_t i) { return "(e:c)"; } + result_t operator()(const hilti::ctor::Bool& b) { return "(c:b)"; } + + void testDispatch(iterator_t::Position i) { + if ( auto s = dispatch(i) ) + x += *s; + else + x += "-"; + x += ","; + } + void testDispatch(const_iterator_t::Position i) { + if ( auto s = dispatch(i) ) + x += *s; + else + x += "-"; + x += ","; + } + + std::string x; + const std::string expected = + "(mo),(id),-,-,(id),(ts),-,-,(id),(ti),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),"; + }; + + + auto root = ast(); + auto v = Visitor(); + for ( auto i : v.walk(root) ) + v.testDispatch(i); + + CHECK(v.x == v.expected); +} + +TEST_CASE("Visitor, post-order") { + struct Visitor : hilti::visitor::PostOrder { + using base_t::base_t; + + result_t operator()(const hilti::Module& m) { x += "(mo)"; } + result_t operator()(const hilti::ID& id) { x += "(id)"; } + result_t operator()(const hilti::Type& t) { x += "(t)"; } + result_t operator()(const hilti::type::String& s) { x += "(ts)"; } + result_t operator()(const hilti::type::SignedInteger& i) { x += "(ti)"; } + result_t operator()(const hilti::expression::Ctor& c) { x += "(e:c)"; } + result_t operator()(const hilti::ctor::Bool& b) { x += "(c:b)"; } + + void testDispatch(iterator_t::Position i) { + if ( ! dispatch(i) ) + x += "-"; + x += ","; + } + void testDispatch(const_iterator_t::Position i) { + if ( ! dispatch(i) ) + x += "-"; + x += ","; + } + + std::string x; + const std::string expected = + "(id),-,(id),(ts)(t),-,-,(id),(ti)(t),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(c:b),(e:c),-,(mo),"; + }; + + auto root = ast(); + auto v = Visitor(); + for ( auto i : v.walk(root) ) + v.testDispatch(i); + + CHECK(v.x == v.expected); +} + +TEST_CASE("Retrieve parent") { + struct Visitor : hilti::visitor::PreOrder { + result_t operator()(const hilti::type::SignedInteger& n, const_position_t i) { x = i.parent().typename_(); } + std::string x; + }; + + auto root = ast(); + auto v = Visitor(); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + REQUIRE(v.x == "hilti::declaration::Type"); +} + +TEST_CASE("Find specific parent") { + struct Visitor : hilti::visitor::PreOrder { + result_t operator()(const hilti::type::SignedInteger& n, const_position_t i) { + x = to_node(i.findParent()).typename_(); + } + std::string x; + }; + + auto root = ast(); + auto v = Visitor(); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + REQUIRE(v.x == "hilti::Module"); +} + +TEST_CASE("Copy node by value") { + hilti::Type t = hilti::type::Vector(hilti::type::String()); + CHECK(! hilti::type::isConstant(t)); + auto t2 = hilti::type::setConstant(t._clone(), true); + auto t3 = hilti::type::setConstant(t, true); + auto t4(hilti::type::setConstant(t, true)); + CHECK(hilti::type::isConstant(t2)); + CHECK(hilti::type::isConstant(t3)); + CHECK(hilti::type::isConstant(t4)); + CHECK(! hilti::type::isConstant(t)); +} diff --git a/scripts/3rdparty/run-clang-format/.clang-format b/scripts/3rdparty/run-clang-format/.clang-format new file mode 100644 index 000000000..b875084e1 --- /dev/null +++ b/scripts/3rdparty/run-clang-format/.clang-format @@ -0,0 +1,12 @@ +# using clang-format version 5.0.0 +Language: Cpp +BasedOnStyle: LLVM + +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +# make adding new members at the end less noisy in diffs +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true diff --git a/scripts/3rdparty/run-clang-format/.travis.yml b/scripts/3rdparty/run-clang-format/.travis.yml new file mode 100644 index 000000000..dd2b57bb6 --- /dev/null +++ b/scripts/3rdparty/run-clang-format/.travis.yml @@ -0,0 +1,12 @@ +language: cpp + +addons: + apt: + sources: + - llvm-toolchain-trusty-5.0 + - key_url: 'http://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - clang-format-5.0 + +script: + - ./run-clang-format.py -r . diff --git a/scripts/3rdparty/run-clang-format/LICENSE b/scripts/3rdparty/run-clang-format/LICENSE new file mode 100644 index 000000000..e728f2488 --- /dev/null +++ b/scripts/3rdparty/run-clang-format/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Guillaume Papin + +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/scripts/3rdparty/run-clang-format/README.rst b/scripts/3rdparty/run-clang-format/README.rst new file mode 100644 index 000000000..c60365069 --- /dev/null +++ b/scripts/3rdparty/run-clang-format/README.rst @@ -0,0 +1,63 @@ +===================== + run-clang-format.py +===================== +---------------------------------------------- + Lint files and directories with clang-format +---------------------------------------------- + +.. contents:: + :local: + +Introduction +============ + +A wrapper script around clang-format, suitable for linting multiple files +and to use for continuous integration. + +This is an alternative API for the clang-format command line. +It runs over multiple files and directories in parallel. +A diff output is produced and a sensible exit code is returned. + +.. image:: screenshot.png + + +How to use? +=========== + +Copy `run-clang-format.py `_ in your project, +then run it recursively on directories, or specific files:: + + ./run-clang-format.py -r src include foo.cpp + +It's possible to exclude paths from the recursive search:: + + ./run-clang-format.py -r \ + --exclude src/third_party \ + --exclude '*_test.cpp' \ + src include foo.cpp + + +Continuous integration +====================== + +Check `.travis.yml <.travis.yml>`_. + +For an example of failure in logs, click the badge (build is broken on purpose): + +.. image:: https://travis-ci.org/Sarcasm/run-clang-format.svg?branch=master + :target: https://travis-ci.org/Sarcasm/run-clang-format + + +FAQ +=== + +Can I check only changed files? +------------------------------- + +No, and this is what this repository was initially about. +However, once working around a few shortcommings of ``git clang-format``, +I opted to try an alternative strategy +which expects the whole project to be correctly formatted. + +It would make sense to support this feature as well, +so that the coding style does not need to be enforced but merely suggested. diff --git a/scripts/3rdparty/run-clang-format/run-clang-format.py b/scripts/3rdparty/run-clang-format/run-clang-format.py new file mode 100755 index 000000000..3d0cca0a9 --- /dev/null +++ b/scripts/3rdparty/run-clang-format/run-clang-format.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python +"""A wrapper script around clang-format, suitable for linting multiple files +and to use for continuous integration. + +This is an alternative API for the clang-format command line. +It runs over multiple files and directories in parallel. +A diff output is produced and a sensible exit code is returned. + +""" + +from __future__ import print_function, unicode_literals + +import argparse +import codecs +import difflib +import fnmatch +import io +import multiprocessing +import os +import signal +import subprocess +import sys +import traceback + +from functools import partial + +DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx' + + +class ExitStatus: + SUCCESS = 0 + DIFF = 1 + TROUBLE = 2 + + +def list_files(files, recursive=False, extensions=None, exclude=None): + if extensions is None: + extensions = [] + if exclude is None: + exclude = [] + + out = [] + for file in files: + if recursive and os.path.isdir(file): + for dirpath, dnames, fnames in os.walk(file): + fpaths = [os.path.join(dirpath, fname) for fname in fnames] + for pattern in exclude: + # os.walk() supports trimming down the dnames list + # by modifying it in-place, + # to avoid unnecessary directory listings. + dnames[:] = [ + x for x in dnames + if + not fnmatch.fnmatch(os.path.join(dirpath, x), pattern) + ] + fpaths = [ + x for x in fpaths if not fnmatch.fnmatch(x, pattern) + ] + for f in fpaths: + ext = os.path.splitext(f)[1][1:] + if ext in extensions: + out.append(f) + else: + out.append(file) + return out + + +def make_diff(file, original, reformatted): + return list( + difflib.unified_diff( + original, + reformatted, + fromfile='{}\t(original)'.format(file), + tofile='{}\t(reformatted)'.format(file), + n=3)) + + +class DiffError(Exception): + def __init__(self, message, errs=None): + super(DiffError, self).__init__(message) + self.errs = errs or [] + + +class UnexpectedError(Exception): + def __init__(self, message, exc=None): + super(UnexpectedError, self).__init__(message) + self.formatted_traceback = traceback.format_exc() + self.exc = exc + + +def run_clang_format_diff_wrapper(args, file): + try: + ret = run_clang_format_diff(args, file) + return ret + except DiffError: + raise + except Exception as e: + raise UnexpectedError('{}: {}: {}'.format(file, e.__class__.__name__, + e), e) + + +def run_clang_format_diff(args, file): + try: + with io.open(file, 'r', encoding='utf-8') as f: + original = f.readlines() + except IOError as exc: + raise DiffError(str(exc)) + invocation = [args.clang_format_executable, file] + + # Use of utf-8 to decode the process output. + # + # Hopefully, this is the correct thing to do. + # + # It's done due to the following assumptions (which may be incorrect): + # - clang-format will returns the bytes read from the files as-is, + # without conversion, and it is already assumed that the files use utf-8. + # - if the diagnostics were internationalized, they would use utf-8: + # > Adding Translations to Clang + # > + # > Not possible yet! + # > Diagnostic strings should be written in UTF-8, + # > the client can translate to the relevant code page if needed. + # > Each translation completely replaces the format string + # > for the diagnostic. + # > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation + # + # It's not pretty, due to Python 2 & 3 compatibility. + encoding_py3 = {} + if sys.version_info[0] >= 3: + encoding_py3['encoding'] = 'utf-8' + + try: + proc = subprocess.Popen( + invocation, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + **encoding_py3) + except OSError as exc: + raise DiffError(str(exc)) + proc_stdout = proc.stdout + proc_stderr = proc.stderr + if sys.version_info[0] < 3: + # make the pipes compatible with Python 3, + # reading lines should output unicode + encoding = 'utf-8' + proc_stdout = codecs.getreader(encoding)(proc_stdout) + proc_stderr = codecs.getreader(encoding)(proc_stderr) + # hopefully the stderr pipe won't get full and block the process + outs = list(proc_stdout.readlines()) + errs = list(proc_stderr.readlines()) + proc.wait() + if proc.returncode: + raise DiffError("clang-format exited with status {}: '{}'".format( + proc.returncode, file), errs) + return make_diff(file, original, outs), errs + + +def bold_red(s): + return '\x1b[1m\x1b[31m' + s + '\x1b[0m' + + +def colorize(diff_lines): + def bold(s): + return '\x1b[1m' + s + '\x1b[0m' + + def cyan(s): + return '\x1b[36m' + s + '\x1b[0m' + + def green(s): + return '\x1b[32m' + s + '\x1b[0m' + + def red(s): + return '\x1b[31m' + s + '\x1b[0m' + + for line in diff_lines: + if line[:4] in ['--- ', '+++ ']: + yield bold(line) + elif line.startswith('@@ '): + yield cyan(line) + elif line.startswith('+'): + yield green(line) + elif line.startswith('-'): + yield red(line) + else: + yield line + + +def print_diff(diff_lines, use_color): + if use_color: + diff_lines = colorize(diff_lines) + if sys.version_info[0] < 3: + sys.stdout.writelines((l.encode('utf-8') for l in diff_lines)) + else: + sys.stdout.writelines(diff_lines) + + +def print_trouble(prog, message, use_colors): + error_text = 'error:' + if use_colors: + error_text = bold_red(error_text) + print("{}: {} {}".format(prog, error_text, message), file=sys.stderr) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + '--clang-format-executable', + metavar='EXECUTABLE', + help='path to the clang-format executable', + default='clang-format') + parser.add_argument( + '--extensions', + help='comma separated list of file extensions (default: {})'.format( + DEFAULT_EXTENSIONS), + default=DEFAULT_EXTENSIONS) + parser.add_argument( + '-r', + '--recursive', + action='store_true', + help='run recursively over directories') + parser.add_argument('files', metavar='file', nargs='+') + parser.add_argument( + '-q', + '--quiet', + action='store_true') + parser.add_argument( + '-j', + metavar='N', + type=int, + default=0, + help='run N clang-format jobs in parallel' + ' (default number of cpus + 1)') + parser.add_argument( + '--color', + default='auto', + choices=['auto', 'always', 'never'], + help='show colored diff (default: auto)') + parser.add_argument( + '-e', + '--exclude', + metavar='PATTERN', + action='append', + default=[], + help='exclude paths matching the given glob-like pattern(s)' + ' from recursive search') + + args = parser.parse_args() + + # use default signal handling, like diff return SIGINT value on ^C + # https://bugs.python.org/issue14229#msg156446 + signal.signal(signal.SIGINT, signal.SIG_DFL) + try: + signal.SIGPIPE + except AttributeError: + # compatibility, SIGPIPE does not exist on Windows + pass + else: + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + + colored_stdout = False + colored_stderr = False + if args.color == 'always': + colored_stdout = True + colored_stderr = True + elif args.color == 'auto': + colored_stdout = sys.stdout.isatty() + colored_stderr = sys.stderr.isatty() + + retcode = ExitStatus.SUCCESS + files = list_files( + args.files, + recursive=args.recursive, + exclude=args.exclude, + extensions=args.extensions.split(',')) + + if not files: + return + + njobs = args.j + if njobs == 0: + njobs = multiprocessing.cpu_count() + 1 + njobs = min(len(files), njobs) + + if njobs == 1: + # execute directly instead of in a pool, + # less overhead, simpler stacktraces + it = (run_clang_format_diff_wrapper(args, file) for file in files) + pool = None + else: + pool = multiprocessing.Pool(njobs) + it = pool.imap_unordered( + partial(run_clang_format_diff_wrapper, args), files) + while True: + try: + outs, errs = next(it) + except StopIteration: + break + except DiffError as e: + print_trouble(parser.prog, str(e), use_colors=colored_stderr) + retcode = ExitStatus.TROUBLE + sys.stderr.writelines(e.errs) + except UnexpectedError as e: + print_trouble(parser.prog, str(e), use_colors=colored_stderr) + sys.stderr.write(e.formatted_traceback) + retcode = ExitStatus.TROUBLE + # stop at the first unexpected error, + # something could be very wrong, + # don't process all files unnecessarily + if pool: + pool.terminate() + break + else: + sys.stderr.writelines(errs) + if outs == []: + continue + if not args.quiet: + print_diff(outs, use_color=colored_stdout) + if retcode == ExitStatus.SUCCESS: + retcode = ExitStatus.DIFF + return retcode + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/3rdparty/run-clang-format/screenshot.png b/scripts/3rdparty/run-clang-format/screenshot.png new file mode 100644 index 000000000..f65102db7 Binary files /dev/null and b/scripts/3rdparty/run-clang-format/screenshot.png differ diff --git a/scripts/3rdparty/run-clang-format/src/foo.cpp b/scripts/3rdparty/run-clang-format/src/foo.cpp new file mode 100644 index 000000000..c3390aea4 --- /dev/null +++ b/scripts/3rdparty/run-clang-format/src/foo.cpp @@ -0,0 +1,7 @@ +#include + +std::string foo(const std::string &a, const std::string &b) { + std::string sum; + sum = a + b; + return sum; +} diff --git a/scripts/autogen-dispatchers b/scripts/autogen-dispatchers new file mode 100755 index 000000000..549654200 --- /dev/null +++ b/scripts/autogen-dispatchers @@ -0,0 +1,64 @@ +#! /usr/bin/env python3 + +import argparse +import string +import sys +import os +import re + +### Main + +def error(msg): + print("error: {}".format(msg), file=sys.stderr) + sys.exit(1) + +parser = argparse.ArgumentParser() +parser.add_argument("nodes", nargs="*", metavar="nodes", action="store", help="nodes.decl file") +parser.add_argument("--output", dest="output", action="store", default=None, required=True, help="Output file") + +args = parser.parse_args() +out = open(args.output, "w") + +print("#define VISITOR_DISPATCHERS \\", file=out) + +traits = {} +nodes = [] + +for f in args.nodes: + for line in open(f): + line = line.strip() + if not line or line.startswith("//"): + continue + + if line.startswith("trait"): + m = line.split() + traits[m[2]] = m[1] + continue + + m = line.split(" : ") + cls = m[0] + trait = m[1] if len(m) > 1 else "isNode" + + nodes.append((cls, trait)) + +for (cls, trait) in nodes: + if not trait in traits: + print("No 'trait' definition for {}".format(trait), file=sys.stderr) + sys.exit(1) + +for (btrait, bcls) in traits.items(): + print(" if constexpr ( std::is_base_of<{}, Erased>::value ) {{ \\".format(bcls), file=out) + + for (cls, trait) in nodes: + if trait != btrait: + continue + + def output(msg): + print(msg.format(cls=cls, trait=trait) + " \\", file=out) + + output(" if ( auto r = do_dispatch_one(n, tn, d, i, no_match_so_far) ) return r;") + + print(" } \\", file=out) + output(""); + +print("", file=out) diff --git a/scripts/autogen-operators-implementations b/scripts/autogen-operators-implementations new file mode 100755 index 000000000..ea9ceb51c --- /dev/null +++ b/scripts/autogen-operators-implementations @@ -0,0 +1,23 @@ +#! /bin/sh + +if [ $# != 2 ]; then + echo "usage: $(basename $0) /path/to/hilti/include/ast/operators" + exit 1 +fi + +echo "#include <$1/ast/all.h>" +echo "" +echo "#include " + +find $2 -type f | grep -v common.h | while read i; do + cat $i | \ + grep -v '#define' | \ + awk '/(BEGIN_|STANDARD).*\($/ { getline x; printf "%s %s", $0, x; next} {print}' | \ + egrep 'BEGIN_|STANDARD' | \ + grep -v ns::op | \ + sort | \ + awk -v ns=$1 -F '[(), ]+' '{ + printf("namespace %s::operator_::%s { static hilti::operator_::Register _operator_%s{%s::Operator::kind(), %s::Operator()}; }\n", ns, $2, $3, $3, $3); + printf("hilti::Expression %s::operator_::%s::%s::Operator::instantiate(const std::vector& operands, const Meta& meta) const { return hilti::expression::ResolvedOperator(%s(*this, operands, meta)); }\n", ns, $2, $3, $3); + }'; +done diff --git a/scripts/autogen-operators-nodes-decl b/scripts/autogen-operators-nodes-decl new file mode 100755 index 000000000..26fb71ca5 --- /dev/null +++ b/scripts/autogen-operators-nodes-decl @@ -0,0 +1,15 @@ +#! /bin/sh + +if [ $# != 2 ]; then + echo "usage: $(basename $0) /path/to/hilti/include/ast/operators" + exit 1 +fi + +find $2 -type f | grep -v common.h | while read i; do + cat $i | \ + grep -v '#define' | \ + awk '/(BEGIN_|STANDARD|OPERATOR_DECLARE_ONLY).*\($/ { getline x; printf "%s %s", $0, x; next} {print}' | \ + egrep 'BEGIN_|STANDARD|OPERATOR_DECLARE_ONLY' | \ + awk -v ns=$1 -F '[(), ]+' '{ printf "%s::operator_::%s::%s : isResolvedOperator\n", ns, $2, $3 }' | \ + grep -v ns::op; +done | sort diff --git a/scripts/autogen-type-erased b/scripts/autogen-type-erased new file mode 100755 index 000000000..5c6b0c1da --- /dev/null +++ b/scripts/autogen-type-erased @@ -0,0 +1,363 @@ +#! /usr/bin/env python3 + +import argparse +import string +import sys +import os +import re + +Template = """ +class ${class}; + +${class_comment}class Concept : public util::type_erasure::ConceptBase { +public: + $concept_ctors + virtual ~Concept() = default; + $concept_methods + $concept_operators + virtual $class _clone() const = 0; + virtual std::shared_ptr _clone_ptr() const = 0; +private: + $concept_private_attrs +}; + +template +using ModelBase = util::type_erasure::ModelBase; + +template +class Model : public ModelBase { +public: + using ModelBase::ModelBase; + using ModelBase::data; + + $model_methods + $class _clone() const final; + std::shared_ptr _clone_ptr() const final; +}; + +using ErasedBase = util::type_erasure::ErasedBase<$class_requires, Concept, Model${attrs_types}>; + +class $class : public ErasedBase$class_traits { +public: + using ErasedBase::ErasedBase; + + $class() noexcept = default; + $class($class&&) noexcept = default; + $class& operator=($class&&) = default; + ~$class() override = default; + ${template_addl} + + $class_methods + $class _clone() const; + + std::shared_ptr _clone_ptr() const; +}; + +$model_methods_impl +template +$class Model::_clone() const { return T(data()); } + +template +std::shared_ptr Model::_clone_ptr() const { + if constexpr ( std::is_base_of::value ) + return nullptr; + else + return std::make_shared(T(data())); +} + +$class_methods_impl +inline $class $class::_clone() const { return data() ? data()->_clone() : $class(); } + +inline std::shared_ptr $class::_clone_ptr() const { + if ( data() ) + return data()->_clone_ptr(); + else + return nullptr; +} + + +""" + +TemplateAddlConst = """ + $class(const $class& other) = default; + $class& operator=(const $class& other) = default; +""" + +TemplateAddlNonConst = """ + $class(const $class& other) { + if ( auto x = other._clone_ptr() ) + ErasedBase::operator=(x); + else + ErasedBase::operator=(other.data()); + } + + $class& operator=(const $class& other) { + if ( this != &other ) { + if ( auto x = other._clone_ptr() ) + ErasedBase::operator=(x); + else + ErasedBase::operator=(other.data()); + } + + return *this; + } +""" + + +def parseVariable(s): + class Variable: + pass + + v = Variable() + (v.type, v.name) = [i.strip() for i in s.rsplit(maxsplit=1)] + return v + + +def varsToString(vars): + return ", ".join("{} {}".format(v.type, v.name) for v in vars) + + +def forwardParam(p): + if p.type.endswith("&"): + return p.name + else: + return "std::move({})".format(p.name) + + +def parseMethod(s): + class Method: + pass + + m = Method() + + i = re.search("(.*) *with *default *(\{.*)$", s) + + if i: + s = i.group(1) + m.default = i.group(2) + else: + m.default = None + + x = re.split("[()]", s) + (m.result, m.name) = [i.strip() for i in x[0].rsplit(sep=None, maxsplit=1)] + m.params = [parseVariable(v) + for v in re.split(" *, *", x[1])] if x[1] else [] + + if len(x) < 3: + m.attrs = "" + m.traits = [] + m.default = "" + else: + y = x[2].rsplit("if", maxsplit=1) + if len(y) >= 2: + m.attrs = y[0].strip() + z = y[1].strip().split("else", maxsplit=1) + if len(z) >= 2: + m.traits = z[0].strip().split(" or ") + m.default = z[1].strip() + else: + m.traits = y[1].strip().split(" or ") + m.default = "" + + else: + m.attrs = x[2].strip() + m.traits = [] + m.default = "" + + return m + + +def parseTrait(s): + class Trait: + pass + + x = line.split() + t = Trait() + t.method = x[1] + assert x[2] == "from" + t.trait = x[3] + + if t.method.endswith("()"): + t.method = t.method[:-2] + + return t + +# Main + + +def error(msg): + print("error: {}".format(msg), file=sys.stderr) + sys.exit(1) + + +parser = argparse.ArgumentParser() +parser.add_argument("api", metavar="file", action="store", + help="File with API specification") +parser.add_argument("--output", dest="output", action="store", + default=None, required=False, help="Output file (default is stdout)") +parser.add_argument("--constant", dest="constant", action="store_true", + required=False, help="Assume instaces of class are non-mutable") + +args = parser.parse_args() +methods = [] +traits = [] +attrs = [] +class_ = None +trait_methods = [] + +in_comment = False +comment = "" + +for line in open(args.api): + line = line.strip() + if not line or line.startswith("//"): + continue + + if line.startswith("/*"): + in_comment = True + comment = "" + + if in_comment: + comment += (line + "\n") + + if line.endswith("*/"): + in_comment = False + if comment: + comment += " " + + continue + + line = line.rstrip(";") + + if line.startswith("class"): + m = line.split() + if len(m) == 3: + (__, class_, _) = m + + if len(m) == 5 and m[2] == ":": + class_ = m[1] + traits = [m[3]] + + assert "(" in class_ + m = re.split("[()]", class_) + class_ = m[0] + class_comment = comment[:-4] + requires = m[1] + comment = "" + continue + + if line.startswith("trait"): + t = parseTrait(line) + trait_methods.append(t) + comment = "" + continue + + if line.startswith("}"): + break + + if class_ and "(" in line: + m = parseMethod(line) + methods.append(m) + m.comment = comment + comment = "" + continue + + if class_: + m = parseVariable(line) + attrs.append(m) + comment = "" + continue + + error("parse error") + sys.exit(1) + +concept_methods = ["{}virtual {} {}({}) {} = 0;".format( + m.comment, m.result, m.name, varsToString(m.params), m.attrs) for m in methods] +concept_methods += ["virtual bool {}() const = 0;".format(t.method) + for t in trait_methods] + +class_methods = ["{} {}({}) {};" .format( + m.result, m.name, varsToString(m.params), m.attrs) for m in methods] +class_methods += ["bool {}() const;" .format(t.method) for t in trait_methods] + +class_methods_impl = ["inline {} {}::{}({}) {} {{ return data()->{}({}); }}\n".format(m.result, class_, m.name, varsToString( + m.params), m.attrs, m.name, ", ".join([forwardParam(p) for p in m.params])) for m in methods] +class_methods_impl += ["inline bool {}::{}() const {{ return data()->{}(); }}\n" .format( + class_, t.method, t.method) for t in trait_methods] + +model_methods = ["{} {}({}) {} final;" .format( + m.result, m.name, varsToString(m.params), m.attrs, m.name) for m in methods] +model_methods += ["bool {}() const final;".format(t.method) + for t in trait_methods] + + +def mmi(m): + result = "return data().{}({});".format( + m.name, ", ".join([a.name for a in m.params])) + # FIXME(bbannier): issue for void? + default = "return {};".format( + m.default) if m.default else "throw std::bad_cast();" + if m.traits: + cexps = ["""constexpr ( std::is_base_of<{}, T>::value ) + {} +""".format(t, result) for t in m.traits] + cexps = " else if ".join(cexps) + return """ + if {} else + {} +""".format(cexps, default) + else: + return result + + +def tmi(t): + return "return std::is_base_of<{}, T>::value;".format(t.trait) + + +model_methods_impl = ["template\n{} Model::{}({}) {} {{ {} }}\n" .format( + m.result, m.name, varsToString(m.params), m.attrs, mmi(m)) for m in methods] +model_methods_impl += ["template\nbool Model::{}() const {{ {} }}\n" .format(t.method, tmi(t)) + for t in trait_methods] + +ctor_attrs = ["_{}(std::move({}))".format(v.name, v.name) for v in attrs] +concept_private_attrs = ["{} _{};".format(v.type, v.name) for v in attrs] + +if attrs: + concept_ctors = ["Concept({}){} {{}}".format( + varsToString(attrs), " : " + ", ".join(ctor_attrs))] +else: + concept_ctors = ["Concept() = default;"] + +concept_ctors += ["Concept(const Concept&) = default;"] +concept_ctors += ["Concept(Concept&&) = default;"] +concept_operators = ["Concept& operator=(const Concept&) = default;"] +concept_operators += ["Concept& operator=(Concept&&) = default;"] +concept_methods += ["const {}& {}() {{ return _{}; }}".format(v.type, + v.name, v.name) for v in attrs] +class_methods += ["const {}& {}() {{ return data()->{}(); }}".format(v.type, + v.name, v.name) for v in attrs] +class_traits = [", public {}".format(t) for t in traits] +class_requires = requires +attrs_types = "".join([(", " + v.type) for v in attrs]) + +vals = { + "class": class_, + "class_comment": class_comment, + "concept_methods": "\n ".join(concept_methods), + "model_methods": "\n ".join(model_methods), + "model_methods_impl": "\n".join(model_methods_impl), + "class_methods": "\n ".join(class_methods), + "class_methods_impl": "\n".join(class_methods_impl), + "class_requires": class_requires, + "class_traits": "".join(class_traits), + "concept_ctors": "\n ".join(concept_ctors), + "concept_operators": "\n ".join(concept_operators), + "concept_private_attrs": "\n ".join(concept_private_attrs), + "attrs_types": attrs_types, + "template_addl": (TemplateAddlConst.strip() if args.constant else TemplateAddlNonConst.strip()) +} + +out = open(args.output, "w") if args.output else sys.stdout + +print(string.Template(Template).substitute( + vals).replace("$class", class_), file=out) diff --git a/scripts/autogen-version b/scripts/autogen-version new file mode 100755 index 000000000..bfc114b86 --- /dev/null +++ b/scripts/autogen-version @@ -0,0 +1,114 @@ +#! /bin/sh +# +# Generates a readable representation of the current version number; with +# --short a short one. Or alternatively with --header, a version.h file with +# various constants defined accordingly. + +usage() { + echo "usage $(basename $0) [--short|--header ] []" +} + +output=long + +if [ "$1" = "--short" ]; then + output=short + shift +fi + +if [ "$1" = "--header" ]; then + test $# -lt 2 && usage && exit 1 + output=header + dst=$2 + shift + shift +fi + +if [ $# != 0 -a $# != 1 ]; then + usage + exit 1 +fi + +ref=HEAD +describe_arg="--dirty" + +test -n "$1" && test "$1" != "HEAD" && ref="$1" && describe_arg="$1" + +branch=$(git symbolic-ref --short "${ref}" 2>/dev/null) + +# When running from CI, for geting the branch name we prefer what +# might be passed in through environment variables as we may not +# actually be on a branch. +test -n "${CI_COMMIT_REF_NAME}" && branch=${CI_COMMIT_REF_NAME} # GitLab +test -n "${CIRRUS_BRANCH}" && branch=${CIRRUS_BRANCH} # Cirrus CI + +describe=$(git describe --always --match "v*" "${describe_arg}" | sed 's/^v//g') + +version=$(echo "${describe}" | awk -F - '{print $1}' | sed 's/^v//g') +commit=$(echo "${describe}" | awk -F - '{print $2}') +hash=$(git rev-parse --short "${ref}") +dirty=$(echo "${describe}" | awk -F - '{print $4}') + +test -n "${commit}" -a -n "${dirty}" && commit="${commit}.${dirty}" + +case "${branch}" in + master) + # As we don't have (actual or anticipated) releases yet, we use the old + # Zeek scheme for now: commits since last tag. + prerelease="${commit}" + # prerelease="dev-${commit}" + ;; + + release/*) + prerelease="${commit}" + ;; + + "") + prerelease="${commit}" + ;; + + *) + prerelease="branch" + hash="" + ;; +esac + +test -n "${prerelease}" && str_prerelease="-${prerelease}" + +str_branch="${branch}" +test -n "${branch}" -a -n "${hash}" && str_branch="${branch} " + +major=$(echo "${version}" | cut -d '.' -f 1) +minor=$(echo "${version}" | cut -d '.' -f 2) +patch=$(echo "${version}" | cut -d '.' -f 3) + +test -z "${major}" && major=0 +test -z "${minor}" && minor=0 +test -z "${patch}" && patch=0 + +version_number=$((${major} * 10000 + ${minor} * 100 + ${patch})) + +if [ "${output}" = "long" ]; then + echo "${version}${str_prerelease} (${str_branch}${hash})" + +elif [ "${output}" = "short" ]; then + echo "${version}${str_prerelease}" + +elif [ "${output}" = "header" ]; then + trap "rm -f '${dst}.tmp'" EXIT + cat >"${dst}.tmp" <" + exit 1 +fi + +$1 -### 2>&1 | grep ^InstalledDir | cut -d ' ' -f 2 diff --git a/scripts/ninja-build-stats b/scripts/ninja-build-stats new file mode 100755 index 000000000..710cd0cf5 --- /dev/null +++ b/scripts/ninja-build-stats @@ -0,0 +1,16 @@ +#!/usr/bin/env gawk -f +# +# From https://github.com/ninja-build/ninja/issues/1080#issuecomment-255436851 + +!/^#/ { + TIMES[$4] += ($2 - $1)/1000 + COUNT[$4] += 1 +} + +END { + for (TGT in TIMES) + AVG[TGT]=TIMES[TGT]/COUNT[TGT] + asorti(AVG, SORTED, "@val_num_desc") + for (num in SORTED) + print AVG[SORTED[num]] " " SORTED[num] +} diff --git a/scripts/run-clang-format b/scripts/run-clang-format new file mode 100755 index 000000000..f04f6bbdc --- /dev/null +++ b/scripts/run-clang-format @@ -0,0 +1,42 @@ +#! /bin/sh +# +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +base=$(dirname $0) +fix=0 + +if [ $# != 0 ]; then + if [ "$1" = "--fixit" ]; then + fix=1 + else + echo "usage: $(basename $0) [--fixit]" + exit 1 + fi +fi + +if [ -z "${CLANG_FORMAT}" ]; then + CLANG_FORMAT=$(which clang-format 2>/dev/null) +fi + +if [ -z "${CLANG_FORMAT}" -o ! -x "${CLANG_FORMAT}" ]; then + echo "Cannot find clang-format. If not in PATH, set CLANG_FORMAT." + exit 1 +fi + +if ! (cd / && ${CLANG_FORMAT} -dump-config | grep -q SpacesInConditionalStatement); then + echo "${CLANG_FORMAT} does not support SpacesInConditionalStatement. Install custom version and put it into PATH, or point CLANG_FORMAT to it." + exit 1 +fi + +if [ ! -e .clang-format ]; then + echo "Must execute in top-level directory." + exit 1 +fi + +cmd="${base}/3rdparty/run-clang-format/run-clang-format.py -r --clang-format-executable ${CLANG_FORMAT} --exclude '*/3rdparty/*' hilti spicy zeek" + +if [ ${fix} = 1 ]; then + eval ${cmd} | git apply -p0 +else + eval ${cmd} +fi diff --git a/scripts/run-clang-tidy b/scripts/run-clang-tidy new file mode 100755 index 000000000..84d67c733 --- /dev/null +++ b/scripts/run-clang-tidy @@ -0,0 +1,138 @@ +#! /bin/sh +# +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +usage() { + echo "Usage: $(basename $0) [--fixit] [-j ] [--clang-tidy-path ] [--clang-tidy-arg ] []" + exit 1 +} + +error() { + echo "$@" >&2 +} + +abspath() { + printf "%s/%s\n" "$(cd $(dirname $1) && pwd)" "$(basename $1)" +} + +cleanup() { + rm -rf "${tmpdir} ${error}" +} + +fix="" +clang_tidy_args="" +files="" +parallel=1 + +if [ -n "${CLANG_TIDY}" ]; then + clang_tidy_path=${CLANG_TIDY} +else + clang_tidy_path=$(which clang-tidy 2>/dev/null) +fi + +while true; do + case "$1" in + --fixit) fix=1; shift;; + --clang-tidy-path) clang_tidy_path="$2"; shift; shift;; + --clang-tidy-arg) clang_tidy_args="${clang_tidy_args} $2"; shift; shift;; + --ignore) ignores="${ignores:+${ignores}|}$2"; shift; shift;; + -j) parallel="$2"; shift; shift;; + -h | --help) usage;; + --) shift; break;; + --*) usage;; + *) break;; + esac +done + +if [ $# -lt 1 ]; then + usage +fi + +build=$(cd $1 && pwd) +root=$(cd ${build}/.. && pwd) +shift + +for f in $@; do + files=$(printf "%s%s\n" ${files} $(abspath ${f})) +done + +cd ${build} + +cmake_cache=CMakeCache.txt +compile_json=compile_commands.json +clang_tidy_ignore=${root}/.clang-tidy.ignore + +if ! which jq >/dev/null 2>&1; then + error "Need jq in PATH, aborting." && exit 1 +fi + +for i in ${cmake_cache} ${compile_json} bin/hiltic; do + if [ ! -f ${i} ]; then + error "${i} not found, did you configure and build?" && exit 1 + fi +done + +if [ -z "${clang_tidy_path}" ]; then + clang_tidy_path=$(cat ${cmake_cache} | grep CMAKE_CXX_COMPILER:PATH | cut -d = -f 2 | sed 's/clang+\{0,2\}/clang-tidy/') +fi + +if [ ! -x "${clang_tidy_path}" ]; then + error "cannot find clang-tidy" && exit 1 +fi + +if [ "${fix}" = 1 -a -n "$(git status --porcelain | egrep -v '^(M|\?\?) ')" ]; then + echo >&2 + echo "error: uncommitted changes in working directory, won't apply changes" >&2 + echo >&2 + git status -sb >&2 + exit 1 +fi + +clang_apply_replacements_path=$(echo ${clang_tidy_path} | sed 's/clang-tidy/clang-apply-replacements/') + +if [ ! -x "${clang_apply_replacements_path}" ]; then + error "cannot find clang-apply-replacements" && exit 1 +fi + +if [ -f "${clang_tidy_ignore}" ]; then + x=$(cat ${clang_tidy_ignore} | egrep -v '^ *(#.*)?$' | awk '{printf "%s|", $1}' | sed 's/|$//g') + ignores="${ignores:+${ignores}|}${x}" +fi + +if [ -z "${ignores}" ]; then + ignores="__NEVER_MATCHES__" +fi + +if [ -z "${files}" ]; then + files=$(cat ${compile_json} | jq -r '.[].file' | egrep '\.(cc|c|h)$' | grep -v autogen/__ | grep -v '\.bif\.' | egrep -v "${root}/(${ignores})") +fi + +cmd="${clang_tidy_path} -quiet -p=${build} ${clang_tidy_args}" + +error=/tmp/$(basename $0).$$.error.tmp +tmpdir=/tmp/$(basename $0).$$.tmp +rm -rf "${tmpdir}" +mkdir -p "${tmpdir}" +trap cleanup EXIT + +base=$(cd ${build}/.. && pwd) +rm -f ${error} + +echo "${files}" | awk -v "cmd=${cmd}" -v "tmp=${tmpdir}" '{t=$1; gsub("[/]", "_", t); printf("%s -export-fixes=%s/%s.yaml %s\n", cmd, tmp, t, $1);}' \ + | tr '\n' '\0' | (xargs -0 -n 1 -P ${parallel} sh -c 2>&1 || touch ${error}) | grep -v 'warnings\? generated\.$' | sed "s#^../\(.*:\)#${base}/\1#g" + +if [ -e "${error}" ]; then + rc=1 +else + rc=0 +fi + +if [ ${rc} != 0 -a -n "${fix}" ]; then + # It looks like clang-apply-replacements can merge & unique changes from + # multiple files. In case that turns out not to work, we could borrow + # from LLVM's run-clang-tidy.py script). That has Python code to merge + # replacements ahead of time. + ${clang_apply_replacements_path} ${tmpdir} +fi + +exit ${rc} diff --git a/scripts/update-license-headers b/scripts/update-license-headers new file mode 100755 index 000000000..3cb4f740b --- /dev/null +++ b/scripts/update-license-headers @@ -0,0 +1,9 @@ +#! /bin/sh + +find . -type d -mindepth 0 -maxdepth 1 | egrep -v '^\.(/build.*|/\.git)(/|$)' | while read dir; do + find "${dir}" -type f | while read file; do + echo ${file} | egrep -q '/3rdparty/|/\..*/|update-license-headers' && continue + cat ${file} | grep -q Copyright || continue + sed -i '' 's/Copyright .* information\./Copyright (c) 2020 by the Zeek Project. See LICENSE for details./' "${file}" + done +done diff --git a/spicy/CMakeLists.txt b/spicy/CMakeLists.txt new file mode 100644 index 000000000..a1bd8470b --- /dev/null +++ b/spicy/CMakeLists.txt @@ -0,0 +1,216 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +##### Compiler library. + +set(AUTOGEN_H "${CMAKE_CURRENT_BINARY_DIR}/include/spicy/autogen") +set(AUTOGEN_RT_H "${CMAKE_CURRENT_BINARY_DIR}/include/spicy/rt/autogen") +set(AUTOGEN_CC "${CMAKE_CURRENT_BINARY_DIR}/src/autogen") +file(MAKE_DIRECTORY "${AUTOGEN_H}" "${AUTOGEN_RT_H}" "${AUTOGEN_CC}") +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" "${CMAKE_CURRENT_BINARY_DIR}/lib") + +set(AUTOGEN_H_HILTI "${CMAKE_CURRENT_BINARY_DIR}/../hilti/include/hilti/autogen") + +FLEX_TARGET(scanner_spicy src/compiler/parser/scanner.ll ${AUTOGEN_CC}/__scanner.cc + DEFINES_FILE ${AUTOGEN_CC}/__scanner.h) +BISON_TARGET(parser_spicy src/compiler/parser/parser.yy ${AUTOGEN_CC}/__parser.cc + DEFINES_FILE ${AUTOGEN_CC}/__parser.h +) + +bison_source(src/compiler/parser/driver.cc ${AUTOGEN_CC}) +bison_source(${AUTOGEN_CC}/__scanner.cc ${AUTOGEN_CC}) +bison_source(${AUTOGEN_CC}/__parser.cc ${AUTOGEN_CC}) + +include(TypeErase) +autogen_type_erased(SOURCES_TYPE_ERASED include/spicy/ast/types/unit-item.api YES) +autogen_type_erased(SOURCES_TYPE_ERASED include/spicy/compiler/detail/codegen/production.api NO) + +include(ASTOperators) +autogen_operators(SOURCES_OPERATORS + spicy + include/spicy/ast/operators + ${AUTOGEN_H}/operators.decl + ${AUTOGEN_CC}/operators-implementations.cc +) + +autogen_dispatchers(SOURCES_TYPE_ERASED ${AUTOGEN_H}/__dispatchers.h + ${CMAKE_CURRENT_SOURCE_DIR}/../hilti/include/hilti/ast/nodes.decl + ${CMAKE_CURRENT_SOURCE_DIR}/include/spicy/ast/nodes.decl + ${AUTOGEN_H_HILTI}/operators.decl + ${AUTOGEN_H}/operators.decl) + +autogen_dispatchers(PRODUCTIONS_TYPE_ERASED ${AUTOGEN_H}/__dispatchers-productions.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/compiler/detail/codegen/productions.decl) + +set(SOURCES_COMPILER + src/ast/types/bitfield.cc + src/ast/types/unit.cc + src/ast/types/unit-items/field.cc + src/ast/types/unit-items/switch.cc + src/compiler/codegen/codegen.cc + src/compiler/codegen/grammar.cc + src/compiler/codegen/grammar-builder.cc + src/compiler/codegen/parser-builder.cc + src/compiler/codegen/parsers/literals.cc + src/compiler/codegen/parsers/types.cc + src/compiler/codegen/production.cc + src/compiler/codegen/productions/look-ahead.cc + src/compiler/codegen/productions/switch.cc + src/compiler/codegen/unit-builder.cc + src/compiler/parser/driver.cc + src/compiler/plugin.cc + src/compiler/visitors/apply-coercions.cc + src/compiler/visitors/coercer.cc + src/compiler/visitors/id-resolver.cc + src/compiler/visitors/printer.cc + src/compiler/visitors/scope-builder.cc + src/compiler/visitors/validator.cc + + ${SOURCES_TYPE_ERASED} + ${SOURCES_OPERATORS} + ${PRODUCTIONS_TYPE_ERASED} + + ${AUTOGEN_CC}/config.cc + ${BISON_parser_spicy_OUTPUTS} + ${FLEX_scanner_spicy_OUTPUTS} + ) + +add_library(spicy ${SOURCES_COMPILER}) +target_compile_options(spicy PRIVATE "-Wall") +target_link_libraries(spicy PRIVATE $,spicy-rt-debug-objects,spicy-rt-objects>) +target_link_libraries(spicy PUBLIC hilti) +target_link_libraries(spicy PRIVATE std::filesystem) +target_include_directories(spicy PUBLIC $) +target_include_directories(spicy PUBLIC $) + +# Unclear why we need this: Without it, the generated Bison/Flex get a broken +# include path on some systems. (Seen on Ubuntu 19.10). +set_target_properties(spicy PROPERTIES NO_SYSTEM_FROM_IMPORTED true) + +##### Runtime library. + +set(SOURCES_RUNTIME + src/rt/base64.cc + src/rt/driver.cc + src/rt/global-state.cc + src/rt/init.cc + src/rt/parser.cc + src/rt/sink.cc + src/rt/util.cc + src/rt/zlib.cc + + src/3rdparty/libb64/src/cdecode.c + src/3rdparty/libb64/src/cencode.c +) + +foreach ( lib spicy-rt spicy-rt-debug ) + add_library(${lib}-objects OBJECT ${SOURCES_RUNTIME}) + target_compile_options(${lib}-objects PRIVATE "-fPIC") + target_link_libraries(${lib}-objects PRIVATE std::filesystem) + target_link_libraries(${lib}-objects PUBLIC ZLIB::ZLIB) + target_include_directories(${lib}-objects PUBLIC $) + target_include_directories(${lib}-objects PUBLIC $) + target_include_directories(${lib}-objects PRIVATE include/3rdparty/libtask) + + add_library(${lib} STATIC) + target_link_libraries(${lib} ${lib}-objects) +endforeach () + +# Build spicy-rt with release flags. +string(REPLACE " " ";" cxx_flags_release ${CMAKE_CXX_FLAGS_RELEASE}) +target_compile_options(spicy-rt-debug-objects PRIVATE ${cxx_flags_release}) +target_compile_options(spicy-rt-objects PRIVATE "-DNDEBUG;-O3;-g0;-Wall") +target_compile_definitions(spicy-rt-objects PRIVATE "HILTI_RT_BUILD_TYPE_RELEASE") +target_link_libraries(spicy-rt-objects PUBLIC hilti-rt-objects) + +# Build spicy-rt-debug with debug flags. +string(REPLACE " " ";" cxx_flags_debug ${CMAKE_CXX_FLAGS_DEBUG}) +target_compile_options(spicy-rt-debug-objects PRIVATE ${cxx_flags_debug}) +target_compile_options(spicy-rt-debug-objects PRIVATE "-UNDEBUG;-O0;-Wall") +target_compile_definitions(spicy-rt-debug-objects PRIVATE "HILTI_RT_BUILD_TYPE_DEBUG") +target_link_libraries(spicy-rt-debug-objects PUBLIC hilti-rt-debug-objects) + +##### Configuration files + +# Spicy library directories +set_config_val(SPICY_CONFIG_LIBRARY_DIRS "!INSTALL!${CMAKE_INSTALL_FULL_DATADIR}/spicy !BUILD!${CMAKE_CURRENT_SOURCE_DIR}/lib") + +# Include directories + +set_config_val(SPICY_CONFIG_RUNTIME_INCLUDE_DIRS_DEBUG "!INSTALL!${CMAKE_INSTALL_FULL_INCLUDEDIR} !BUILD!${CMAKE_CURRENT_SOURCE_DIR}/include !BUILD!${CMAKE_CURRENT_BINARY_DIR}/include") +set_config_val(SPICY_CONFIG_RUNTIME_INCLUDE_DIRS_RELEASE "!INSTALL!${CMAKE_INSTALL_FULL_INCLUDEDIR} !BUILD!${CMAKE_CURRENT_SOURCE_DIR}/include !BUILD!${CMAKE_CURRENT_BINARY_DIR}/include") + +# CXX flags + +set_config_val(SPICY_CONFIG_RUNTIME_CXX_FLAGS_DEBUG "") +set_config_val(SPICY_CONFIG_RUNTIME_CXX_FLAGS_RELEASE "") + +# Libraries + +set_config_val(SPICY_CONFIG_RUNTIME_LIBRARIES_DEBUG "spicy-rt-debug z") +set_config_val(SPICY_CONFIG_RUNTIME_LIBRARIES_RELEASE "spicy-rt z") + +# Library directories + +set_config_val(SPICY_CONFIG_RUNTIME_LIBRARY_DIRS_DEBUG "") +set_config_val(SPICY_CONFIG_RUNTIME_LIBRARY_DIRS_RELEASE "") + +# LD flags + +set_config_val(SPICY_CONFIG_RUNTIME_LD_FLAGS_DEBUG "") +set_config_val(SPICY_CONFIG_RUNTIME_LD_FLAGS_RELEASE "") + +# Generate configurarion + +configure_file(include/config.h.in ${AUTOGEN_H}/config.h) +configure_file(include/rt/config.h.in ${AUTOGEN_RT_H}/config.h) +configure_file(src/config.cc.in ${AUTOGEN_CC}/config.cc) + +##### Binaries + +add_executable(spicyc bin/spicyc.cc) +target_compile_options(spicyc PRIVATE "-Wall") +target_link_libraries(spicyc PRIVATE spicy) + +add_executable(spicy-config bin/spicy-config.cc) +target_compile_options(spicy-config PRIVATE "-Wall") +target_link_libraries(spicy-config PRIVATE spicy) + +add_executable(spicy-driver bin/spicy-driver.cc) +target_compile_options(spicy-driver PRIVATE "-Wall") +target_link_libraries(spicy-driver PRIVATE spicy) + +add_executable(spicy-doc bin/spicy-doc.cc) +target_compile_options(spicy-doc PRIVATE "-Wall") +target_link_libraries(spicy-doc PRIVATE spicy) + +add_custom_target(spicy-build ALL) +add_custom_command(TARGET spicy-build + POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bin/spicy-build + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + +if ( HILTI_USE_JIT ) + add_executable(spicy-driver bin/spicy-driver.cc) + target_link_libraries(spicy-driver PRIVATE spicy) +endif () + + +## Tests +add_executable(spicy-tests + tests/main.cc + tests/grammar.cc) +target_link_libraries(spicy-tests PRIVATE spicy doctest) +target_compile_options(spicy-tests PRIVATE "-Wall") +add_test(NAME spicy-tests COMMAND ${CMAKE_BINARY_DIR}/bin/spicy-tests) + +## Installation + +install(TARGETS spicy LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS spicy-rt spicy-rt-debug ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS spicyc spicy-config spicy-driver RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(PROGRAMS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/spicy-build DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/lib/spicy-driver-host.cc DESTINATION ${CMAKE_INSTALL_DATADIR}/spicy) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib/ DESTINATION ${CMAKE_INSTALL_DATADIR}/spicy MESSAGE_NEVER) + +install_headers(include spicy) +install_headers(${CMAKE_CURRENT_BINARY_DIR}/include/spicy spicy) +install(CODE "file(REMOVE ${CMAKE_INSTALL_FULL_INCLUDEDIR}/spicy/spicy)") # Get rid of symlink diff --git a/spicy/bin/spicy-build b/spicy/bin/spicy-build new file mode 100755 index 000000000..3d7f9dd3e --- /dev/null +++ b/spicy/bin/spicy-build @@ -0,0 +1,140 @@ +#! /bin/sh +# +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +usage() { + cat << EOF +$(basename $0) [options] + + -d Build a debug version. + -o Destination name for the compiled executable; default is "a.out". + -S Do not compile the "spicy-driver" host application into executable. + -v Verbose output, display command lines executing. + -t Do not delete tmp files (useful for inspecting, and use with debugger) + +Input files may be anything that spicyc can compile to C++. + +EOF +} + +mktmp() { + base=$1 + ext=$2 + cnt=0 + while : ; do + tmp=${dest}/${base}.${cnt}.tmp.${ext} + test -e ${tmp} || break + cnt=$((cnt + 1)) + done + touch ${tmp} + echo ${tmp} +} + +mkcc() { + tmp=$(mktmp $1 cc) + echo ${tmp} +} + +execute() { + # Remove absolute path of executable + test "${verbose}" = 1 && echo $@ | sed 's#^[^ ]*/\([a-zA-Z+_-]\{1,\}\) #> \1 #' + $@ +} + +cleanup() { + test "${delete_tmps}" = 1 && rm -rf "${dest}" +} + +### Main + +hiltic_flags="" +spicy_config_flags="" +verbose=0 +delete_tmps=1 +compile_spicy_driver=1 +out=a.out + +while getopts "hdo:Stv" opt; do + case "$opt" in + d) + hiltic_flags="${hiltic_flags} -d" + spicy_config_flags="--debug" + ;; + + o) out=$OPTARG;; + S) compile_spicy_driver=0;; + t) delete_tmps=0;; + v) verbose=1;; + + h) usage; exit 0;; + *) usage; exit 1;; + esac +done + +shift $((${OPTIND} - 1)) + +inputs=$@ +test -z "${inputs}" && usage && exit 1 +test -z "${out}" && usage && exit 1 + +base=$(cd $(dirname $0)/.. && pwd) +dest=$(pwd)/${out}.$$.tmp.d +rm -rf "${dest}" +mkdir -p "${dest}" + +trap cleanup EXIT + +for i in $(which hilti-config 2>/dev/null) ${base}/bin/hilti-config ${base}/build/bin/hilti-config; do + test -x $i && hilti_config=$i && break +done + +for i in $(which spicy-config 2>/dev/null) ${base}/bin/spicy-config ${base}/build/bin/spicy-config; do + test -x $i && spicy_config=$i && break +done + +if [ -z "${hilti_config}" ]; then + echo cannot find hilti-config + exit 1 +fi + +if [ -z "${spicy_config}" ]; then + echo cannot find spicy-config + exit 1 +fi + +cxx="$(${spicy_config} --cxx ${spicy_config_flags}) $(${spicy_config} --cxxflags ${spicy_config_flags})" +ldflags="$(${spicy_config} --ldflags ${spicy_config_flags})" +spicy_lib_dir=$(${spicy_config} --libdirs | sed 's/^\. //g') # A bit of a hack to get the dir we want ... +spicy_driver=${spicy_lib_dir}/spicy-driver-host.cc + +rt_modules="" +cxxs="" + +if [ "${compile_spicy_driver}" = 1 ]; then + rt_modules="${spicy_lib_dir}/filter.spicy" + cxxs="${spicy_driver}" +fi + +n=0 +for i in ${inputs} ${rt_modules}; do + case "${i}" in + *.cc) + cc=${i} + ;; + *) + # Generate C++ code for HILTI/Spicy source. + cc=$(mkcc ${out}) + execute $(${spicy_config} --spicyc) ${hiltic_flags} -c -o ${cc} ${i} || exit 1 + ;; + esac + + cxxs="${cxxs} ${cc}" +done + +# Generate C++ linker code +cc=$(mkcc ${out}) +execute $(${spicy_config} --spicyc) ${hiltic_flags} -l -o ${cc} ${cxxs} || exit 1 +cxxs="${cxxs} ${cc}" + +# Compile all C++ code into executable +execute ${cxx} ${cxxs} ${ldflags} -o ${out} || exit 1 diff --git a/spicy/bin/spicy-config.cc b/spicy/bin/spicy-config.cc new file mode 100644 index 000000000..9ce55d011 --- /dev/null +++ b/spicy/bin/spicy-config.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// Outputs paths and flags for using Spicy. + +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace std; + +void usage() { + std::cerr << R"( +Usage: spicy-config [options] + +Available options: + + --build Prints "debug" or "release", depending on the build configuration. + --bindir Prints the path to the directory where binaries are installed. + --cxx Print the path to the C++ compiler used to build Spicy + --cxxflags Print flags for C++ compiler. (These are addition to any that HILTI needs.) + --debug Output flags for working with debugging versions. + --distbase Print path of the Spicy source distribution. + --help Print this usage summary + --jit-compiler Prints the version of the JIT compiler if compiled with corresponding support. + --jit-support Prints 'yes' if compiled with JIT support, 'no' otherwise. + --ldflags Print flags for linker. (These are addition to any that HILTI needs.) + --libdirs Print standard Spicy library directories. + --prefix Print path of installation (TODO: same as --distbase currently) + --spicy-build Print the path to the spicy-build script. + --spicyc Print the path to the spicyc binary. + --zeek Print the path to the Zeek executable + --zeek-prefix Print the path to the Zeek installation prefix + --zeek-plugin-path Print the path to go into ZEEK_PLUGIN_PATH for enabling the Zeek Spicy plugin + --version Print Spicy version. + +)"; +} + +template +void join(std::vector& a, const std::vector& b) { + a.insert(a.end(), b.begin(), b.end()); +} + +int main(int argc, char** argv) { + bool want_debug = false; + + std::list cxxflags; + std::list ldflags; + + std::list options; + + // First pass over arguments: look for control options. + + for ( int i = 1; i < argc; i++ ) { + string opt = argv[i]; + + if ( opt == "--help" || opt == "-h" ) { + usage(); + return 0; + } + + if ( opt == "--debug" ) { + want_debug = true; + continue; + } + + options.push_back(opt); + } + + spicy::configuration().extendHiltiConfiguration(); + + std::vector result; + + for ( const auto& opt : options ) { + if ( opt == "--distbase" ) { + result.emplace_back(hilti::configuration().distbase); + continue; + } + + if ( opt == "--prefix" ) { + result.emplace_back(hilti::configuration().install_prefix); + continue; + } + + if ( opt == "--version" ) { + result.emplace_back(hilti::configuration().version_string_long); + continue; + } + + if ( opt == "--build" ) { +#ifndef NDEBUG + result.emplace_back("debug"); +#else + result.emplace_back("release"); +#endif + continue; + } + + if ( opt == "--bindir" ) { + result.emplace_back(spicy::configuration().spicyc.parent_path()); + continue; + } + + if ( opt == "--jit-compiler" ) { + result.emplace_back(hilti::JIT::compilerVersion()); + continue; + } + + if ( opt == "--jit-support" ) { + result.emplace_back(hilti::configuration().jit_enabled ? "yes" : "no"); + continue; + } + + if ( opt == "--cxx" ) { + result.emplace_back(hilti::configuration().cxx); + continue; + } + + if ( opt == "--spicyc" ) { + result.emplace_back(spicy::configuration().spicyc); + continue; + } + + if ( opt == "--spicy-build" ) { + result.emplace_back((spicy::configuration().spicyc.parent_path() / "spicy-build")); + continue; + } + + if ( opt == "--zeek" ) { +#ifdef HAVE_ZEEK + result.emplace_back(ZEEK_EXECUTABLE); + continue; +#else + exit(1); +#endif + } + + if ( opt == "--zeek-prefix" ) { +#ifdef HAVE_ZEEK + result.emplace_back(ZEEK_PREFIX); + continue; +#else + exit(1); +#endif + } + + if ( opt == "--zeek-plugin-path" ) { +#ifdef HAVE_ZEEK + if ( hilti::configuration().uses_build_directory ) + result.emplace_back(hilti::configuration().build_directory / "zeek/plugin"); + else + result.emplace_back(hilti::configuration().lib_directory / "spicy/Zeek_Spicy"); + + continue; +#else + exit(1); +#endif + } + + if ( opt == "--libdirs" ) { + join(result, spicy::configuration().spicy_library_paths); + continue; + } + + if ( opt == "--cxxflags" ) { + if ( want_debug ) + join(result, hilti::configuration().runtime_cxx_flags_debug); + else + join(result, hilti::configuration().runtime_cxx_flags_release); + + continue; + } + + if ( opt == "--ldflags" ) { + if ( want_debug ) + join(result, hilti::configuration().runtime_ld_flags_debug); + else + join(result, hilti::configuration().runtime_ld_flags_release); + + continue; + } + + std::cerr << "spicy-config: unknown option " << opt << "; use --help to see list." << std::endl; + return 1; + } + + cout << util::join(result.begin(), result.end(), " ") << std::endl; + + return 0; +} diff --git a/spicy/bin/spicy-doc.cc b/spicy/bin/spicy-doc.cc new file mode 100644 index 000000000..7ad96223c --- /dev/null +++ b/spicy/bin/spicy-doc.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include + +// Force linking of the Spicy library. (Note to self: Don't used an inlined +// function here). +auto _ = ::spicy::configuration(); + +using nlohmann::json; + +template +static std::string to_string(const T& t) { + return std::string(hilti::Node(t)); +} + +static std::string formatType(const hilti::Type& t) { + if ( auto d = t.tryAs() ) + return d->description(); + + return to_string(t); +} + +#define KIND_TO_STRING(k) \ + case k: return util::split(#k, "::").back(); + +static std::string kindToString(hilti::operator_::Kind kind) { + switch ( kind ) { + KIND_TO_STRING(hilti::operator_::Kind::Add); + KIND_TO_STRING(hilti::operator_::Kind::Begin); + KIND_TO_STRING(hilti::operator_::Kind::BitAnd); + KIND_TO_STRING(hilti::operator_::Kind::BitOr); + KIND_TO_STRING(hilti::operator_::Kind::BitXor); + KIND_TO_STRING(hilti::operator_::Kind::Call); + KIND_TO_STRING(hilti::operator_::Kind::Cast); + KIND_TO_STRING(hilti::operator_::Kind::DecrPostfix); + KIND_TO_STRING(hilti::operator_::Kind::DecrPrefix); + KIND_TO_STRING(hilti::operator_::Kind::Delete); + KIND_TO_STRING(hilti::operator_::Kind::Deref); + KIND_TO_STRING(hilti::operator_::Kind::Difference); + KIND_TO_STRING(hilti::operator_::Kind::DifferenceAssign); + KIND_TO_STRING(hilti::operator_::Kind::Division); + KIND_TO_STRING(hilti::operator_::Kind::DivisionAssign); + KIND_TO_STRING(hilti::operator_::Kind::Equal); + KIND_TO_STRING(hilti::operator_::Kind::End); + KIND_TO_STRING(hilti::operator_::Kind::Greater); + KIND_TO_STRING(hilti::operator_::Kind::GreaterEqual); + KIND_TO_STRING(hilti::operator_::Kind::HasMember); + KIND_TO_STRING(hilti::operator_::Kind::In); + KIND_TO_STRING(hilti::operator_::Kind::IncrPostfix); + KIND_TO_STRING(hilti::operator_::Kind::IncrPrefix); + KIND_TO_STRING(hilti::operator_::Kind::Index); + KIND_TO_STRING(hilti::operator_::Kind::Lower); + KIND_TO_STRING(hilti::operator_::Kind::LowerEqual); + KIND_TO_STRING(hilti::operator_::Kind::Member); + KIND_TO_STRING(hilti::operator_::Kind::MemberCall); + KIND_TO_STRING(hilti::operator_::Kind::Modulo); + KIND_TO_STRING(hilti::operator_::Kind::Multiple); + KIND_TO_STRING(hilti::operator_::Kind::MultipleAssign); + KIND_TO_STRING(hilti::operator_::Kind::Negate); + KIND_TO_STRING(hilti::operator_::Kind::New); + KIND_TO_STRING(hilti::operator_::Kind::Power); + KIND_TO_STRING(hilti::operator_::Kind::ShiftLeft); + KIND_TO_STRING(hilti::operator_::Kind::ShiftRight); + KIND_TO_STRING(hilti::operator_::Kind::SignNeg); + KIND_TO_STRING(hilti::operator_::Kind::SignPos); + KIND_TO_STRING(hilti::operator_::Kind::Size); + KIND_TO_STRING(hilti::operator_::Kind::Sum); + KIND_TO_STRING(hilti::operator_::Kind::SumAssign); + KIND_TO_STRING(hilti::operator_::Kind::TryMember); + KIND_TO_STRING(hilti::operator_::Kind::Unequal); + KIND_TO_STRING(hilti::operator_::Kind::Unpack); + KIND_TO_STRING(hilti::operator_::Kind::Unknown); + + default: util::cannot_be_reached(); + } +} + +static json operandToJSON(const hilti::operator_::Operand& o) { + json op; + + hilti::Type t; + + if ( auto f = + std::get_if(const std::vector&, + const std::vector&)>>(&o.type) ) + t = *(*f)({}, {}); + else + t = std::get(o.type); + + op["type"] = formatType(hilti::type::nonConstant(t)); + op["const"] = hilti::type::isConstant(t); + + if ( o.id ) + op["id"] = std::string(*o.id); + else + op["id"] = nullptr; + + op["optional"] = o.optional; + + if ( o.default_ ) + op["default"] = to_string(*o.default_); + else + op["default"] = nullptr; + + if ( o.doc ) + op["doc"] = *o.doc; + else + op["doc"] = nullptr; + + return op; +} + +int main(int argc, char** argv) { + json all_operators; + + auto operators = hilti::operator_::registry().all(); + + for ( const auto& [kind, operators] : operators ) { + for ( const auto& o : operators ) { + json operator_; + operator_["kind"] = kindToString(o.kind()); + operator_["doc"] = o.doc(); + operator_["namespace"] = o.docNamespace(); + operator_["rtype"] = formatType(o.result({})); + + if ( o.kind() == hilti::operator_::Kind::MemberCall ) { + auto operands = o.operands(); + auto self = operands[0]; + auto method = std::get(operands[1].type).as(); + + if ( ! std::get(operands[2].type).isA() ) + continue; // XXX + + auto params = std::get(operands[2].type).as(); + + operator_["self"] = operandToJSON(self); + operator_["id"] = method.id(); + operator_["args"] = std::list(); + + for ( const auto& p : params.operands() ) + operator_["args"].push_back(operandToJSON(p)); + } + else { + operator_["operands"] = json(); + + for ( const auto& x : o.operands() ) + operator_["operands"].push_back(operandToJSON(x)); + } + + all_operators.push_back(std::move(operator_)); + } + } + + std::cout << all_operators.dump(4) << std::endl; + return 0; +} diff --git a/spicy/bin/spicy-driver.cc b/spicy/bin/spicy-driver.cc new file mode 100644 index 000000000..0a3685543 --- /dev/null +++ b/spicy/bin/spicy-driver.cc @@ -0,0 +1,243 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +#include +#include + +#include +#include + +using spicy::rt::fmt; + +static struct option long_driver_options[] = {{"abort-on-exceptions", required_argument, nullptr, 'A'}, + {"compiler-debug", required_argument, nullptr, 'D'}, + {"debug", no_argument, nullptr, 'd'}, + {"debug-addl", required_argument, nullptr, 'X'}, + {"disable-jit", no_argument, nullptr, 'J'}, + {"help", no_argument, nullptr, 'h'}, + {"increment", required_argument, nullptr, 'i'}, + {"library-path", required_argument, nullptr, 'L'}, + {"list-parsers", no_argument, nullptr, 'l'}, + {"optimize", no_argument, nullptr, 'O'}, + {"parser", required_argument, nullptr, 'p'}, + {"report-times", required_argument, nullptr, 'R'}, + {"show-backtraces", required_argument, nullptr, 'B'}, + {"skip-dependencies", no_argument, nullptr, 'S'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +static void fatalError(const std::string& msg) { + hilti::logger().error(fmt("spicy-driver: %s", msg)); + exit(1); +} + +class SpicyDriver : public hilti::Driver, public spicy::rt::Driver { +public: + SpicyDriver(const std::string_view& argv0 = "") : hilti::Driver("spicy-driver", argv0) { + spicy::Configuration::extendHiltiConfiguration(); + } + + void parseOptions(int argc, char** argv); + void usage(); + + bool opt_list_parsers = false; + int opt_increment = 0; + std::string opt_parser; + +private: + void hookInitRuntime() override { spicy::rt::init(); } + void hookFinishRuntime() override { spicy::rt::done(); } +}; + +void SpicyDriver::usage() { + auto exts = util::join(hilti::plugin::registry().supportedExtensions(), ", "); + + std::cerr + << "Usage: cat | spicy-driver [options] ...\n" + "\n" + "Options:\n" + "\n" + " -A | --abort-on-exceptions When executing compiled code, abort() instead of throwing HILTI " + "exceptions.\n" + " -B | --show-backtraces Include backtraces when reporting unhandled exceptions.\n" + " -D | --compiler-debug Activate compile-time debugging output for given debug streams " + "(comma-separated; 'help' for list).\n" + " -L | --library-path Add path to list of directories to search when importing modules.\n" + " -O | --optimize Build optimized release version of generated code.\n" + " -d | --debug Include debug instrumentation into generated code.\n" + " -i | --increment Feed data incrementenally in chunks of size n.\n" + " -l | --list-parsers List available parsers and exit.\n" + " -p | --parser Use parser to process input. Only neeeded if more than one parser " + "is available.\n" + " -R | --report-times Report a break-down of compiler's execution time.\n" + " -S | --skip-dependencies Do not automatically compile dependencies during JIT.\n" + " -v | --version Print version information.\n" + " -X | --debug-addl Implies -d and adds selected additional instrumentation " + "(comma-separated; see 'help' for list).\n" + "\n" + "Environment variables:\n" + "\n" + " SPICY_PATH Colon-separated list of directories to search for modules. In contrast " + "to --library-paths using this flag overwrites builtin paths.\n" + "\n" + "Inputs can be " + << exts + << ", .cc/.cxx, *.o.\n" + "\n"; +} + +void SpicyDriver::parseOptions(int argc, char** argv) { + hilti::driver::Options driver_options; + hilti::Options compiler_options; + +#ifdef HILTI_HAVE_JIT + driver_options.execute_code = true; +#endif + driver_options.include_linker = true; + driver_options.logger = std::make_unique(); + + while ( true ) { + int c = getopt_long(argc, argv, "ABD:hdJX:OVlp:i:SRL:", long_driver_options, nullptr); + + if ( c < 0 ) + break; + + switch ( c ) { + case 'A': driver_options.abort_on_exceptions = true; break; + + case 'B': driver_options.show_backtraces = true; break; + + case 'd': { + compiler_options.debug = true; + break; + } + + case 'X': { + auto arg = std::string(optarg); + + if ( arg == "help" ) { + std::cerr << "Additional debug instrumentation:\n"; + std::cerr << " flow: log function calls to debug stream \"hilti-flow\"\n"; + std::cerr << " location: log statements to debug stream \"hilti-trace\"\n"; + std::cerr << " trace: track current source code location for error reporting\n"; + std::cerr << "\n"; + exit(0); + } + + compiler_options.debug = true; + + if ( auto r = compiler_options.parseDebugAddl(arg); ! r ) + fatalError(r.error()); + + break; + } + + case 'J': { + driver_options.execute_code = false; + break; + } + + case 'D': { + auto arg = std::string(optarg); + + if ( arg == "help" ) { + std::cerr << "Debug streams:\n"; + + for ( const auto& s : hilti::logging::DebugStream::all() ) + std::cerr << " " << s << "\n"; + + std::cerr << "\n"; + exit(0); + } + + for ( const auto& s : util::split(arg, ",") ) { + if ( ! driver_options.logger->debugEnable(s) ) + fatalError(fmt("unknown debug stream '%s', use 'help' for list", arg)); + } + + break; + } + + case 'i': + opt_increment = atoi(optarg); // NOLINT + break; + + case 'l': opt_list_parsers = true; break; + + case 'p': opt_parser = optarg; break; + + case 'O': compiler_options.optimize = true; break; + + case 'R': driver_options.report_times = true; break; + + case 'S': driver_options.skip_dependencies = true; break; + + case 'v': std::cerr << "spicy-driver v" << hilti::configuration().version_string_long << std::endl; exit(0); + + case 'h': usage(); exit(0); + + case 'L': compiler_options.library_paths.emplace_back(optarg); break; + + default: usage(); fatalError(fmt("option %c not supported", c)); + } + } + + setCompilerOptions(compiler_options); + setDriverOptions(std::move(driver_options)); + + initialize(); + + while ( optind < argc ) { + if ( auto rc = addInput(argv[optind++]); ! rc ) + fatalError(rc.error().description()); + } +} + +int main(int argc, char** argv) { + SpicyDriver driver; + + driver.parseOptions(argc, argv); + + if ( auto x = driver.compile(); ! x ) + // The main error messages have been reported already at this point. + // The returned error will have some more info about which pass + // failed in its description, however that's less interesting to the + // user so we're just reporting a generic message here. + fatalError("aborting after errors"); + + try { + if ( auto x = driver.initRuntime(); ! x ) + fatalError(x.error().description()); + + if ( driver.opt_list_parsers ) + driver.listParsers(std::cout); + + else { + auto parser = driver.lookupParser(driver.opt_parser); + if ( ! parser ) + fatalError(parser.error()); + + std::ifstream in("/dev/stdin", std::ios::in | std::ios::binary); + + if ( ! in.is_open() ) + fatalError("cannot open stdin for reading"); + + driver.processInput(**parser, in, driver.opt_increment); + driver.finishRuntime(); + } + + } catch ( const std::exception& e ) { + std::cerr << util::fmt("[fatal error] terminating with uncaught exception of type %s: %s", + util::demangle(typeid(e).name()), e.what()) + << std::endl; + exit(1); + } + + if ( driver.driverOptions().report_times ) + util::timing::summary(std::cerr); + + return 0; +} diff --git a/spicy/bin/spicyc.cc b/spicy/bin/spicyc.cc new file mode 100644 index 000000000..cf9ef2379 --- /dev/null +++ b/spicy/bin/spicyc.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include + +class Spicyc : public hilti::Driver { +public: + Spicyc(const std::string_view& argv0 = "") : hilti::Driver("spicyc", argv0) { + spicy::Configuration::extendHiltiConfiguration(); + } + + void hookInitRuntime() override { spicy::rt::init(); } + void hookFinishRuntime() override { spicy::rt::done(); } +}; + +int main(int argc, char** argv) { + Spicyc driver; + + if ( auto rc = driver.parseOptions(argc, argv); ! rc ) { + hilti::logger().error(rc.error().description()); + exit(1); + } + + if ( auto rc = driver.run(); ! rc ) { + hilti::logger().error(rc.error().description()); + exit(1); + } + + return 0; +} diff --git a/spicy/include/3rdparty/libb64 b/spicy/include/3rdparty/libb64 new file mode 120000 index 000000000..39dbd026f --- /dev/null +++ b/spicy/include/3rdparty/libb64 @@ -0,0 +1 @@ +../../src/3rdparty/libb64/include \ No newline at end of file diff --git a/spicy/include/ast/aliases.h b/spicy/include/ast/aliases.h new file mode 100644 index 000000000..65702ec9d --- /dev/null +++ b/spicy/include/ast/aliases.h @@ -0,0 +1,85 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spicy { + +using Attribute = hilti::Attribute; +using AttributeSet = hilti::AttributeSet; +using Ctor = hilti::Ctor; +using Declaration = hilti::Declaration; +using Expression = hilti::Expression; +using Function = hilti::Function; +using ID = hilti::ID; +using Location = hilti::Location; +using Meta = hilti::Meta; +using Module = hilti::Module; +using Node = hilti::Node; +using NodeRef = hilti::NodeRef; +using Operator = hilti::Operator; +using Statement = hilti::Statement; +using Type = hilti::Type; + +namespace declaration { +using Linkage = hilti::declaration::Linkage; + +namespace parameter { +using Kind = hilti::declaration::parameter::Kind; +} // namespace parameter + +} // namespace declaration + +namespace function { +using CallingConvention = hilti::function::CallingConvention; +} // namespace function + +namespace type { +using namespace hilti::type; +} // namespace type + +#if 0 +namespace type { +using Function = hilti::type::Function; +using Void = hilti::type::Void; +using Wildcard = hilti::type::Wildcard; +using Bytes = hilti::type +} +#endif + +#if 0 +namespace type::function { +using Parameter = hilti::type::function::Parameter; +using Result = hilti::type::function::Result; +using Flavor = hilti::type::function::Flavor; +} +#endif + +namespace node { + +using None = hilti::node::None; +static const Node none = None::create(); + +using Properties = hilti::node::Properties; + +template +bool isEqual(const T* this_, const Other& other) { + return hilti::node::isEqual(this_, other); +} + +} // namespace node +} // namespace spicy diff --git a/spicy/include/ast/all.h b/spicy/include/ast/all.h new file mode 100644 index 000000000..b1db02f47 --- /dev/null +++ b/spicy/include/ast/all.h @@ -0,0 +1,13 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/spicy/include/ast/ctors/all.h b/spicy/include/ast/ctors/all.h new file mode 100644 index 000000000..46598732a --- /dev/null +++ b/spicy/include/ast/ctors/all.h @@ -0,0 +1,3 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once diff --git a/spicy/include/ast/declarations/all.h b/spicy/include/ast/declarations/all.h new file mode 100644 index 000000000..690728dac --- /dev/null +++ b/spicy/include/ast/declarations/all.h @@ -0,0 +1,5 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include diff --git a/spicy/include/ast/declarations/unit-hook.h b/spicy/include/ast/declarations/unit-hook.h new file mode 100644 index 000000000..836045ba7 --- /dev/null +++ b/spicy/include/ast/declarations/unit-hook.h @@ -0,0 +1,60 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace spicy { +namespace declaration { + +/** AST node for a declaration of an external (i.e., module-level) unit hook. */ +class UnitHook : public hilti::NodeBase, public hilti::trait::isDeclaration { +public: + UnitHook(ID id, Type unit, const type::unit::Item& hook, Meta m = Meta()) + : NodeBase(hilti::nodes(std::move(id), std::move(unit), hook), std::move(m)) { + if ( ! hook.isA() ) + hilti::logger().internalError("non-unit hook passed into declaration::UnitHook"); + } + + std::optional unitType() const { + Type t = type::effectiveType(childs()[1].as()); + + if ( auto x = t.tryAs() ) + t = x->dereferencedType(); + + if ( t.isA() ) + return t.as(); + + if ( t.isA() ) + return t.originalNode()->as(); + + // Not resolved yet. + return {}; + } + + auto unitHook() const { return child(2); } + + bool operator==(const UnitHook& other) const { + return unitType() == other.unitType() && unitHook() == other.unitHook(); + } + + /** Implements `Declaration` interface. */ + bool isConstant() const { return true; } + /** Implements `Declaration` interface. */ + auto id() const { return child(0); } + /** Implements `Declaration` interface. */ + Linkage linkage() const { return Linkage::Private; } + /** Implements `Declaration` interface. */ + std::string displayName() const { return "unit hook"; }; + /** Implements `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace declaration +} // namespace spicy diff --git a/spicy/include/ast/detail/visitor.h b/spicy/include/ast/detail/visitor.h new file mode 100644 index 000000000..e32ba1b42 --- /dev/null +++ b/spicy/include/ast/detail/visitor.h @@ -0,0 +1,13 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +// Order of includes is important here. + +#include + +#include +#include + +#include +#include diff --git a/spicy/include/ast/engine.h b/spicy/include/ast/engine.h new file mode 100644 index 000000000..eb6645722 --- /dev/null +++ b/spicy/include/ast/engine.h @@ -0,0 +1,30 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy { + +/** Enum specifying the direction of a unit field's processing. */ +enum class Engine { + Parser, /**< field is being parsed */ + Composer, /**< field is being composed */ + All /**< field is being parsed and composed */ +}; + +namespace detail { +constexpr util::enum_::Value engines[] = { + {Engine::Parser, "parser"}, + {Engine::Composer, "composer"}, + {Engine::All, "parser/composer"}, +}; +} // namespace detail + +constexpr auto to_string(Engine f) { return util::enum_::to_string(f, detail::engines); } + +namespace engine { +constexpr auto from_string(const std::string_view& s) { return util::enum_::from_string(s, detail::engines); } +} // namespace engine + +} // namespace spicy diff --git a/spicy/include/ast/expressions/all.h b/spicy/include/ast/expressions/all.h new file mode 100644 index 000000000..46598732a --- /dev/null +++ b/spicy/include/ast/expressions/all.h @@ -0,0 +1,3 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once diff --git a/spicy/include/ast/hook.h b/spicy/include/ast/hook.h new file mode 100644 index 000000000..303586344 --- /dev/null +++ b/spicy/include/ast/hook.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace spicy { + +/** AST node representing a Spicy unit hook. */ +class Hook : public Function { +public: + Hook(const std::vector& params, std::optional body, Engine engine, + std::optional attrs = {}, Meta m = Meta()) + : Function(ID(""), + type::Function(type::function::Result(type::Void(), m), params, type::function::Flavor::Hook, m), + std::move(body), hilti::function::CallingConvention::Standard, std::move(attrs), std::move(m)), + _engine(engine) {} + + Hook() = default; + + Engine engine() const { return _engine; } + bool isForEach() const { return AttributeSet::find(attributes(), "foreach").has_value(); } + bool isDebug() const { return AttributeSet::find(attributes(), "%debug").has_value(); } + + std::optional priority() const { + if ( auto p = AttributeSet::find(attributes(), "priority") ) + return *p->valueAs(); + + return {}; + } + + bool operator==(const Hook& other) const { + return static_cast(*this) == static_cast(other) && // NOLINT (cppcoreguidelines-slicing) + _engine == other._engine; + } + + auto properties() const { return Function::properties() + node::Properties{{"engine", to_string(_engine)}}; } + +private: + Engine _engine = {}; +}; + +/** Creates an AST node representing a `Hook`. */ +inline Node to_node(Hook f) { return Node(std::move(f)); } + +} // namespace spicy diff --git a/spicy/include/ast/nodes.decl b/spicy/include/ast/nodes.decl new file mode 100644 index 000000000..63b1e5ffb --- /dev/null +++ b/spicy/include/ast/nodes.decl @@ -0,0 +1,22 @@ + +trait spicy::type::unit::Item isUnitItem + +spicy::Hook : isNode +spicy::type::unit::Item : isNode +spicy::type::unit::item::switch_::Case : isNode + +spicy::declaration::UnitHook : isDeclaration + +spicy::type::Bitfield : isType +spicy::type::Sink : isType +spicy::type::Unit : isType + +spicy::type::unit::item::Field : isUnitItem +spicy::type::unit::item::Sink : isUnitItem +spicy::type::unit::item::Switch : isUnitItem +spicy::type::unit::item::UnitHook : isUnitItem +spicy::type::unit::item::UnresolvedField : isUnitItem +spicy::type::unit::item::Variable : isUnitItem + +spicy::statement::Print : isStatement +spicy::statement::Stop : isStatement diff --git a/spicy/include/ast/operators/all.h b/spicy/include/ast/operators/all.h new file mode 100644 index 000000000..895f8c490 --- /dev/null +++ b/spicy/include/ast/operators/all.h @@ -0,0 +1,7 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include diff --git a/spicy/include/ast/operators/bitfield.h b/spicy/include/ast/operators/bitfield.h new file mode 100644 index 000000000..ff493c601 --- /dev/null +++ b/spicy/include/ast/operators/bitfield.h @@ -0,0 +1,74 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace spicy { +namespace operator_ { + +namespace bitfield::detail { + +// Returns an operand as a member expression. +static hilti::expression::Member memberExpression(const Expression& op) { + if ( auto c = op.tryAs() ) + return c->expression().as(); + + return op.as(); +} + +// Checks if an operand refers to a valid field inside a bitfield. +static inline void checkName(const Expression& op0, const Expression& op1) { + auto id = memberExpression(op1).id().local(); + + if ( auto f = op0.type().as().bits(id); ! f ) + hilti::logger().error(util::fmt("bitfield type does not have attribute '%s'", id)); +} + +static inline Type itemType(const Expression& op0, const Expression& op1) { + if ( auto st = op0.type().tryAs() ) { + if ( auto f = st->bits(memberExpression(op1).id().local()) ) + return f->type(); + } + + return type::unknown; +} + +} // namespace bitfield::detail + +BEGIN_OPERATOR_CUSTOM(bitfield, Member) + Type result(const std::vector& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::constant(type::Bitfield(type::Wildcard())), .doc = "bitfield"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const hilti::expression::ResolvedOperator& i, hilti::operator_::const_position_t /* p */) const { + detail::checkName(i.op0(), i.op1()); + } + + std::string doc() const { + return R"( +Retrieves the value of a bitfield's attribute. This is the value of the +corresponding bits inside the underlying integer value, shifted to the very +right. +)"; + } +END_OPERATOR_CUSTOM_x + +} // namespace operator_ +} // namespace spicy diff --git a/spicy/include/ast/operators/sink.h b/spicy/include/ast/operators/sink.h new file mode 100644 index 000000000..bddab3912 --- /dev/null +++ b/spicy/include/ast/operators/sink.h @@ -0,0 +1,264 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spicy::operator_ { + +STANDARD_OPERATOR_1x(sink, SizeValue, Size, type::UnsignedInteger(64), type::constant(type::Sink()), R"( +Returns the number of bytes written into the sink so far. If the sink has +filters attached, this returns the value after filtering. +)"); + +STANDARD_OPERATOR_1x(sink, SizeReference, Size, type::UnsignedInteger(64), hilti::type::StrongReference(type::Sink()), + R"( +Returns the number of bytes written into the referenced sink so far. If the sink has +filters attached, this returns the value after filtering. +)"); + +BEGIN_METHOD(sink, Close) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "close", + .args = {}, + .doc = R"( +Closes a sink by disconnecting all parsing units. Afterwards, the sink's state +is as if it had just been created (so new units can be connected). Note that a +sink it automatically closed when the unit it is part of is done parsing. Also +note that a previously connected parsing unit can *not* be reconnected; trying +to do so will still thrown a ``UnitAlreadyConnected`` exception. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, Connect) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "connect", + .args = {{.id = "u", + .type = type::StrongReference(type::Unit(type::Wildcard()))}}, + .doc = R"( +Connects a parsing unit to a sink. All subsequent write operations to the sink will pass their +data on to this parsing unit. Each unit can only be connected to a single sink. If +the unit is already connected, a ``UnitAlreadyConnected`` exception is thrown. +However, a sink can have more than one unit connected to it. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, ConnectMIMETypeString) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "connect_mime_type", + .args = {{.id = "mt", .type = type::String()}}, + .doc = R"( +Connects parsing units to a sink for all parsers that support a given MIME type. +All subsequent write operations to the sink will pass their data on to these +parsing units. The MIME type may have wildcards for type or subtype, and will +then connect units for all matching parsers. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, ConnectMIMETypeBytes) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "connect_mime_type", + .args = {{.id = "mt", .type = type::Bytes()}}, + .doc = R"( +Connects parsing units to a sink for all parsers that support a given MIME type. +All subsequent write operations to the sink will pass their data on to these +parsing units. The MIME type may have wildcards for type or subtype, and will +then connect units for all matching parsers. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, ConnectFilter) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = hilti::type::Void(), + .id = "connect_filter", + .args = {{.id = "filter", + .type = hilti::type::StrongReference( + spicy::type::Unit(type::Wildcard()))}}, + .doc = R"( +Connects a filter unit to the sink that will transform it's input transparently +before forwaring it for parsing to other connected units. + +Multiple filters can be added to a sink, in which case they will be chained +into a pipeline and the data is passed through them in the order they have been +added. The parsing will then be carried out on the output of the last filter in +the chain. + +Filters must be added before the first data chunk is written into the sink. If +data has already been written when a filter is added, an error is triggered. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, Gap) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "gap", + .args = {{.id = "seq", .type = type::UnsignedInteger(64)}, + {.id = "len", .type = type::UnsignedInteger(64)}}, + .doc = R"( +Reports a gap in the input stream. *seq* is the sequence number of the first +byte missing, *len* is the length of the gap. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, SequenceNumber) + auto signature() const { + return hilti::operator_::Signature{.self = type::constant(spicy::type::Sink()), + .result = type::UnsignedInteger(64), + .id = "sequence_number", + .args = {}, + .doc = R"( +Returns the current sequence number of the sink's input stream, which is one +beyond all data that has been put in order and delivered so far. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, SetAutoTrim) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "set_auto_trim", + .args = {{.id = "enable", .type = type::Bool()}}, + .doc = R"( +Enables or disables auto-trimming. If enabled (which is the default) sink input +data is trimmed automatically once in-order and procssed. See ``trim()`` for +more information about trimming. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, SetInitialSequenceNumber) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "set_initial_sequence_number", + .args = + { + {.id = "seq", .type = type::UnsignedInteger(64)}, + }, + .doc = R"( +Sets the sink's initial sequence number. All sequence numbers given to other +methods are then assumed to be absolute numbers beyond that initial number. If +the initial number is not set, the sink implicitly uses zero instead. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, SetPolicy) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "set_policy", + .args = + { + {.id = "policy", + .type = + type::Enum(type::Wildcard())}, // TODO(robin): Specify full type + }, + .doc = R"( +Sets a sink's reassembly policy for ambigious input. As long as data hasn't +been trimmed, a sink detects overlapping chunks. The policy decides how to +handle ambigious overlaps. The default (and currently only ...) policy is +``ReassemblerPolicy::First``, which resolved ambigiuities by taking the data +from chunk that came first. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, Skip) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "skip", + .args = + { + {.id = "seq", .type = type::UnsignedInteger(64)}, + }, + .doc = R"( +Skips ahead in the input stream. *seq* is is the sequence number where to +continue parsing. If there's still data buffered before that position it will +be ignored, and if auto-skip is on also immediately deleted. If new data is +passed in later before *seq*, that will likewise be ignored. If the input stream +is currently stuck inside a gap, and *seq* is beyond that gap, the stream will +resume processing at *seq*. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, Trim) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "trim", + .args = + { + {.id = "seq", .type = type::UnsignedInteger(64)}, + }, + .doc = R"( +Deletes all data that's still buffered internally up to *seq*. If processing the +input stream hasn't reached *seq* yet, parsing will also skip ahead to *seq*. + +Trimming the input stream releases the memory, but means that the sink won't be +able to detect any further data mismatches. + +Note that by default, auto-trimming is enabled, which means all data is trimmed +automatically once in-order and procssed. +)"}; + } +END_METHOD + +BEGIN_METHOD(sink, Write) + auto signature() const { + return hilti::operator_::Signature{.self = spicy::type::Sink(), + .result = type::Void(), + .id = "write", + .args = {{.id = "data", .type = type::Bytes()}, + {.id = "seq", .type = type::UnsignedInteger(64), .optional = true}, + {.id = "len", .type = type::UnsignedInteger(64), .optional = true}}, + .doc = R"( +Passes data on to all connected parsing units. Multiple *write* calls act like +passing input in incrementally: The units will parse the pieces as if they were +a single stream of data. If no sequence number *seq* is provided, the data is +assumed to represent a chunk to be appended to the current end of the input +stream. If a sequence number is provided, out-of-order data will be buffered +and reassembled before passing on. If *len* is provided, the data is assumed +to represent that many bytes inside the sequence space; if not provided, *len* +defaults to the length of *data*. + +If no units are connected, the call does not have any effect. If multple are +connected and one parsing unit throws an exception, parsing of subsequent units +does not proceed. Note that the order in which the data is parssed to each unit +is undefined, though. + +.. todo:: The error semantics for multiple units aren't great. + +)"}; + } +END_METHOD + +} // namespace spicy::operator_ diff --git a/spicy/include/ast/operators/unit.h b/spicy/include/ast/operators/unit.h new file mode 100644 index 000000000..ae38a4a00 --- /dev/null +++ b/spicy/include/ast/operators/unit.h @@ -0,0 +1,118 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace spicy::operator_ { + +BEGIN_METHOD(unit, Offset) + auto signature() const { + return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + .result = hilti::type::UnsignedInteger(64), + .id = "offset", + .args = {}, + .doc = R"( +Returns the offset of the current location in the input stream relative to the +unit's start. If executed from inside a field hook, the offset will represent +the first byte that the field has been parsed from. If this method is called +before the unit's parsing has begun at all, it will throw a runtime exception. +Once parsing has started, the offset will remain available for the unit's entire +life time. + +Usage of this method requires the unit to be declared with the `%random-access` +property. +)"}; + } +END_METHOD + +BEGIN_METHOD(unit, Input) + auto signature() const { + return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + .result = hilti::type::stream::Iterator(), + .id = "input", + .args = {}, + .doc = R"( +Returns an iterator refering to the input location where the current unit has +begun parsing. If this method is called before the units parsing has begun, it +will throw a runtime exception. Once available, the input position will remain +accessible for the unit's entire life time. + +Usage of this method requires the unit to be declared with the `%random-access` +property. +)"}; + } +END_METHOD + +BEGIN_METHOD(unit, SetInput) + auto signature() const { + return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + .result = hilti::type::Void(), + .id = "set_input", + .args = {{.id = "i", + .type = type::constant(hilti::type::stream::Iterator())}}, + .doc = R"( +Moves the current parsing position to *i*. The new position *i* must be located +inside the range between the first bytes of the current unit (i.e., the result +of ``self.input()``) and the current parsing position. If the new position is +outside of that range, the method will throw a runtime exception. + +Usage of this method requires the unit to be declared with the `%random-access` +property. +)"}; + } +END_METHOD + +BEGIN_METHOD(unit, ConnectFilter) + auto signature() const { + return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + .result = hilti::type::Void(), + .id = "connect_filter", + .args = {{.id = "filter", + .type = hilti::type::StrongReference( + spicy::type::Unit(type::Wildcard()))}}, + .doc = R"( +Connects a separate filter unit to transform the unit's input transparently +before parsing. The filter unit will see the original input, and this unit will +receive everything the filter passes on through `forward()`. + +Filters can be connected only before a unit's parsing begins. The latest +possible point is from inside the target unit's `%init` hook. +)"}; + } +END_METHOD + +BEGIN_METHOD(unit, Forward) + auto signature() const { + return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + .result = hilti::type::Void(), + .id = "forward", + .args = {{.id = "data", .type = hilti::type::Bytes()}}, + .doc = R"( +If the unit is connected as a filter to another one, this method forwards +transformed input over to that other one to parse. If the unit is not connected, +this method will silently discard the data. +)"}; + } +END_METHOD + +BEGIN_METHOD(unit, ForwardEod) + auto signature() const { + return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + .result = hilti::type::Void(), + .id = "forward_eod", + .args = {}, + .doc = R"( +If the unit is connected as a filter to another one, this method signals that +other one that end of its input has been reached. If the unit is not connected, +this method will not do anything. +)"}; + } +END_METHOD + +} // namespace spicy::operator_ diff --git a/spicy/include/ast/statements/all.h b/spicy/include/ast/statements/all.h new file mode 100644 index 000000000..20ae77bfa --- /dev/null +++ b/spicy/include/ast/statements/all.h @@ -0,0 +1,6 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include diff --git a/spicy/include/ast/statements/print.h b/spicy/include/ast/statements/print.h new file mode 100644 index 000000000..954510e84 --- /dev/null +++ b/spicy/include/ast/statements/print.h @@ -0,0 +1,31 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy { +namespace statement { + +class Print : public hilti::NodeBase, public hilti::trait::isStatement { +public: + Print(std::vector e, Meta m = Meta()) + : hilti::NodeBase(hilti::nodes(std::move(e)), std::move(m)) {} + + auto expressions() const { return childs(0, -1); } + + bool operator==(const Print& /* other */) const { + // return expressions() == other.expressions(); FIXME + return false; + } + + // Statement interface. + auto isEqual(const hilti::Statement& other) const { return hilti::node::isEqual(this, other); } + + // Node interface. + auto properties() const { return hilti::node::Properties{}; } +}; + +} // namespace statement +} // namespace spicy diff --git a/spicy/include/ast/statements/stop.h b/spicy/include/ast/statements/stop.h new file mode 100644 index 000000000..47b8951e0 --- /dev/null +++ b/spicy/include/ast/statements/stop.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy { +namespace statement { + +/** AST node for Spicy's `stop` statement. */ +class Stop : public hilti::NodeBase, public hilti::trait::isStatement { +public: + Stop(Meta m = Meta()) : hilti::NodeBase(std::move(m)) {} + + bool operator==(const Stop& /* other */) const { return true; } + + // Statement interface. + auto isEqual(const hilti::Statement& other) const { return hilti::node::isEqual(this, other); } + + // Node interface. + auto properties() const { return hilti::node::Properties{}; } +}; + +} // namespace statement +} // namespace spicy diff --git a/spicy/include/ast/types/all.h b/spicy/include/ast/types/all.h new file mode 100644 index 000000000..0997ab5d8 --- /dev/null +++ b/spicy/include/ast/types/all.h @@ -0,0 +1,9 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include diff --git a/spicy/include/ast/types/bitfield.h b/spicy/include/ast/types/bitfield.h new file mode 100644 index 000000000..01e867efe --- /dev/null +++ b/spicy/include/ast/types/bitfield.h @@ -0,0 +1,124 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spicy { +namespace type { + +namespace bitfield { + +/** AST node for a bitfield element. */ +class Bits : public hilti::NodeBase { +public: + Bits() : NodeBase({ID(""), hilti::node::none}, Meta()) {} + Bits(ID id, int lower, int upper, int field_width, std::optional attrs = {}, Meta m = Meta()) + : hilti::NodeBase(nodes(std::move(id), std::move(attrs)), std::move(m)), + _lower(lower), + _upper(upper), + _field_width(field_width) {} + + auto id() const { return child(0); } + auto lower() const { return _lower; } + auto upper() const { return _upper; } + Type type() const; + auto attributes() const { return childs()[1].tryAs(); } + + /** Implements the `Node` interface. */ + auto properties() const { + return node::Properties{ + {"lower", _lower}, + {"upper", _upper}, + {"field_width", _field_width}, + }; + } + + bool operator==(const Bits& other) const { + return id() == other.id() && _lower == other._lower && _upper == other._upper && + _field_width == other._field_width && attributes() == other.attributes(); + } + + /** + * Copies an existing bits instance but replaces its attributes. + * + * @param f original instance + * @param attrs new attributes + * @return new instances with attributes replaced + */ + static Bits setAttributes(const Bits& f, const AttributeSet& attrs) { + auto x = Bits(f); + x.childs()[1] = attrs; + return x; + } + +private: + int _lower{0}; + int _upper{0}; + int _field_width{0}; +}; + +inline hilti::Node to_node(Bits f) { return hilti::Node(std::move(f)); } + +} // namespace bitfield + +/** AST node for a struct type. */ +class Bitfield : public hilti::TypeBase, + hilti::type::trait::isAllocable, + hilti::type::trait::isParameterized, + hilti::type::trait::isMutable { +public: + Bitfield(int width, std::vector bits, Meta m = Meta()) + : TypeBase(nodes(std::move(bits)), std::move(m)), _width(width) {} + Bitfield(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({}, std::move(m)), _wildcard(true) {} + + int width() const { return _width; } + auto bits() const { return childsOfType(); } + std::optional bits(const ID& id) const; + std::optional bitsIndex(const ID& id) const; + Type type() const; + + bool operator==(const Bitfield& other) const { return width() == other.width() && bits() == other.bits(); } + + /** For internal use by the builder API only. */ + auto _bitsNodes() { return nodesOfType(); } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto typeParameters() const { return util::slice(childs(), 1); } + /** Implements the `Type` interface. */ + auto isWildcard() const { return _wildcard; } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + /** + * Copies an existing type and adds a new field to the copy. + * + * @param s original type + * @param f field to add + * @return new typed with field added + */ + static Bitfield addField(const Bitfield& s, bitfield::Bits f) { + auto x = Type(s)._clone().as(); + x.addChild(std::move(f)); + return x; + } + +private: + int _width = 0; + bool _wildcard = false; +}; + +} // namespace type +} // namespace spicy diff --git a/spicy/include/ast/types/sink.h b/spicy/include/ast/types/sink.h new file mode 100644 index 000000000..5019d7c0c --- /dev/null +++ b/spicy/include/ast/types/sink.h @@ -0,0 +1,24 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy { +namespace type { + +/** AST node for a Sink type. */ +class Sink : public hilti::TypeBase, hilti::type::trait::isAllocable { +public: + Sink(Meta m = Meta()) : TypeBase(std::move(m)) {} + + bool operator==(const Sink& /* other */) const { return true; } + + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace spicy diff --git a/spicy/include/ast/types/unit-item.api b/spicy/include/ast/types/unit-item.api new file mode 100644 index 000000000..2b76361c5 --- /dev/null +++ b/spicy/include/ast/types/unit-item.api @@ -0,0 +1,30 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +class Item(trait::isUnitItem) : hilti::trait::isNode { + /** Returns the type of the parsed unit item. */ + Type itemType() const; + + /** Returns true if the unit item is equivalent to another one. */ + bool isEqual(const Item& other) const; + + /** Implements the `Node` interface. */ + hilti::node::Properties properties() const; + + /** Implements the `Node` interface. */ + const std::vector& childs() const; + + /** Implements the `Node` interface. */ + std::vector& childs(); + + /** Implements the `Node` interface. */ + const Meta& meta() const; + + /** Implements the `Node` interface. */ + void setMeta(Meta m); + + /** Implements the `Node` interface. */ + const NodeRef& originalNode() const; + + /** Implements the `Node` interface. */ + void setOriginalNode(const NodeRef& n); +}; diff --git a/spicy/include/ast/types/unit-item.h b/spicy/include/ast/types/unit-item.h new file mode 100644 index 000000000..fd2f9d353 --- /dev/null +++ b/spicy/include/ast/types/unit-item.h @@ -0,0 +1,76 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace spicy { + +namespace trait { +/** Trait for classes implementing the `Item` interface. */ +class isUnitItem : public hilti::trait::isNode {}; +} // namespace trait + +namespace type { +namespace unit { +namespace detail { + +#include + +/** Creates an AST node representing a `Item`. */ +inline Node to_node(Item i) { return Node(std::move(i)); } + +/** Renders a unit item as Spicy source code. */ +inline std::ostream& operator<<(std::ostream& out, Item d) { return out << to_node(std::move(d)); } + +} // namespace detail + +using Item = detail::Item; +using detail::to_node; + +namespace item { +/** Constructs an AST node from any class implementing the `Item` interface. */ +template::value>* = nullptr> +inline Node to_node(T t) { + return Node(Item(std::move(t))); +} + +} // namespace item +} // namespace unit +} // namespace type +} // namespace spicy + +inline bool operator==(const spicy::type::unit::Item& x, const spicy::type::unit::Item& y) { + if ( &x == &y ) + return true; + + assert(x.isEqual(y) == y.isEqual(x)); // Expected to be symmetric. + return x.isEqual(y); +} + +// TODO(robin): Not clear why we need this. Without it, vector comparisions dont' +// seem to find the eleement comparision operator. +inline bool operator==(const std::vector& t1, const std::vector& t2) { + if ( &t1 == &t2 ) + return true; + + if ( t1.size() != t2.size() ) + return false; + + for ( auto i = std::make_pair(t1.cbegin(), t2.cbegin()); i.first != t1.end() && i.second != t2.end(); + ++i.first, ++i.second ) + if ( ! (*i.first == *i.second) ) + return false; + + return true; +} + +inline bool operator!=(const spicy::type::unit::Item& d1, const spicy::type::unit::Item& d2) { return ! (d1 == d2); } + +inline bool operator!=(const std::vector& t1, const std::vector& t2) { + return ! (t1 == t2); +} diff --git a/spicy/include/ast/types/unit-items/all.h b/spicy/include/ast/types/unit-items/all.h new file mode 100644 index 000000000..e143cae99 --- /dev/null +++ b/spicy/include/ast/types/unit-items/all.h @@ -0,0 +1,10 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include diff --git a/spicy/include/ast/types/unit-items/field.h b/spicy/include/ast/types/unit-items/field.h new file mode 100644 index 000000000..97cbd22b0 --- /dev/null +++ b/spicy/include/ast/types/unit-items/field.h @@ -0,0 +1,119 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include + +namespace spicy::type::unit::item { + +namespace detail { + +static inline Type adaptType(Type type, const std::optional& repeat) { + if ( repeat ) + return type::Vector(type, type.meta()); + + return type; +} + +} // namespace detail + +/** AST node for a unit field. */ +class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + Field(const std::optional& id, Type type, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, + Meta m = Meta()) + : NodeBase(nodes((id ? id : _uniquer.get("anon")), detail::adaptType(std::move(type), repeat), node::none, + repeat, std::move(attrs), std::move(cond), args, sinks, std::move(hooks)), + std::move(m)), + _is_anonynmous(! id.has_value()), + _engine(e), + _args_start(6), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + Field(const std::optional& id, Ctor ctor, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, + Meta m = Meta()) + : NodeBase(nodes((id ? id : _uniquer.get("anon")), detail::adaptType(ctor.type(), repeat), ctor, repeat, + std::move(attrs), std::move(cond), args, sinks, std::move(hooks)), + std::move(m)), + _is_anonynmous(! id.has_value()), + _engine(e), + _args_start(6), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + Field(const std::optional& id, Item item, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, + const Meta& m = Meta()) + : NodeBase(nodes((id ? id : _uniquer.get("anon")), detail::adaptType(item.itemType(), repeat), item, repeat, + std::move(attrs), std::move(cond), args, sinks, std::move(hooks)), + m), + _is_anonynmous(! id.has_value()), + _engine(e), + _args_start(6), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + auto id() const { return childs()[0].as(); } + auto ctor() const { return childs()[2].tryAs(); } + auto vectorItem() const { return childs()[2].tryAs(); } + auto repeatCount() const { return childs()[3].tryAs(); } + auto attributes() const { return childs()[4].tryAs(); } + auto condition() const { return childs()[5].tryAs(); } + auto arguments() const { return childs(_args_start, _args_end); } + auto sinks() const { return childs(_sinks_start, _sinks_end); } + auto hooks() const { return childs(_sinks_end, -1); } + Engine engine() const { return _engine; } + + auto isContainer() const { return parseType().isA(); } + auto isTransient() const { return _is_anonynmous; } + + Type parseType() const; + + Node& ctorNode() { return childs()[2]; } + Node& vectorItemNode() { return childs()[2]; } + Node& typeNode() { return childs()[1]; } + + bool operator==(const Field& other) const { + return _engine == other._engine && id() == other.id() && _originalType() == other._originalType() && + attributes() == other.attributes() && arguments() == other.arguments() && sinks() == other.sinks() && + condition() == other.condition() && hooks() == other.hooks(); + } + + // Unit item interface + Type itemType() const; + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{{"engine", to_string(_engine)}}; } + +private: + Type _originalType() const { return child(1); } + + bool _is_anonynmous; + Engine _engine; + const int _args_start; + const int _args_end; + const int _sinks_start; + const int _sinks_end; + + static inline util::Uniquer _uniquer; +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit-items/property.h b/spicy/include/ast/types/unit-items/property.h new file mode 100644 index 000000000..432701f98 --- /dev/null +++ b/spicy/include/ast/types/unit-items/property.h @@ -0,0 +1,37 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace spicy::type::unit::item { + +/** AST node for a unit property. */ +class Property : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + Property(ID id, bool inherited = false, Meta m = Meta()) + : NodeBase(nodes(std::move(id), node::none), std::move(m)), _inherited(inherited) {} + + Property(ID id, Expression attr, bool inherited = false, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(attr)), std::move(m)), _inherited(inherited) {} + + auto id() const { return child(0); } + auto expression() const { return childs()[1].tryAs(); } + bool interited() const { return _inherited; } + + bool operator==(const Property& other) const { return id() == other.id() && expression() == other.expression(); } + + // Unit field interface + Type itemType() const { return type::Void(); } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{{"inherited", _inherited}}; } + +private: + bool _inherited; +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit-items/sink.h b/spicy/include/ast/types/unit-items/sink.h new file mode 100644 index 000000000..2c3db31e0 --- /dev/null +++ b/spicy/include/ast/types/unit-items/sink.h @@ -0,0 +1,34 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace spicy::type::unit::item { + +/** + * AST node for a unit sink. + */ +class Sink : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + Sink(ID id, std::optional attrs = {}, Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(attrs)), std::move(m)) {} + + auto id() const { return child(0); } + auto attributes() const { return childs()[1].tryAs(); } + + bool operator==(const Sink& other) const { return id() == other.id() && attributes() == other.attributes(); } + + // Unit field interface + Type itemType() const { return type::Sink(meta()); } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{}; } +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit-items/switch.h b/spicy/include/ast/types/unit-items/switch.h new file mode 100644 index 000000000..8616152cc --- /dev/null +++ b/spicy/include/ast/types/unit-items/switch.h @@ -0,0 +1,112 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +namespace spicy::type::unit::item { + +namespace switch_ { + +/** AST node for a unit switch's case. */ +class Case : public hilti::NodeBase { +public: + Case(std::vector exprs, std::vector items, Meta m = Meta()) + : NodeBase(hilti::nodes(std::move(items), std::move(exprs)), std::move(m)) {} + + /** Constructor for a default case. */ + Case(std::vector items, Meta m = Meta()) + : NodeBase(hilti::nodes(std::move(items)), std::move(m)) {} + + /** Constructor for look-ahead case. */ + Case(type::unit::Item field, Meta m = Meta()) + : NodeBase(hilti::nodes(std::move(field)), std::move(m)), _look_ahead(true) {} + + Case() = default; + + auto expressions() const { return childsOfType(); } + auto items() const { return childsOfType(); } + auto itemNodes() { return nodesOfType(); } + + /** Returns true if this is the default case. */ + bool isDefault() const { return expressions().empty() && ! _look_ahead; } + + /** Returns true if this is a look-ahead case. */ + bool isLookAhead() const { return _look_ahead; } + + auto properties() const { return node::Properties{{"default", isDefault()}, {"look-ahead", isLookAhead()}}; } + + bool operator==(const Case& other) const { + return expressions() == other.expressions() && items() == other.items(); + } + +private: + bool _look_ahead = false; +}; + +inline Node to_node(Case c) { return Node(std::move(c)); } + +} // namespace switch_ + +/** AST node for a unit field. */ +class Switch : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + Switch(std::optional expr, const std::vector& cases, Engine e, + std::optional cond, std::vector hooks, Meta m = Meta()) + : NodeBase(nodes(std::move(expr), std::move(cond), cases, std::move(hooks)), std::move(m)), + _engine(e), + _cases_start(2), + _cases_end(_cases_start + static_cast(cases.size())), + _hooks_start(_cases_end), + _hooks_end(-1) {} + + auto expression() const { + return childs()[0].tryAs(); + ; + } + Engine engine() const { return _engine; } + auto condition() const { return childs()[1].tryAs(); } + auto cases() const { return childs(_cases_start, _cases_end); } + auto cases() { return childs(_cases_start, _cases_end); } + auto casesNodes() { return nodesOfType(); } + + auto hooks() const { return childs(_hooks_start, _hooks_end); } + + /** Returns true if there's no field storing information. */ + bool hasNoFields() const; + + /** + * Returns the case that an field is part of, if any. + * + * i: The field. + */ + std::optional case_(const type::unit::item::Field& x); + + bool operator==(const Switch& other) const { + return expression() == other.expression() && engine() == other.engine() && condition() == other.condition() && + cases() == other.cases() && hooks() == other.hooks(); + } + + // Unit item interface + Type itemType() const { return type::Void(); } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{{"engine", to_string(_engine)}}; } + +private: + Engine _engine; + const int _cases_start; + const int _cases_end; + const int _hooks_start; + const int _hooks_end; +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit-items/unit-hook.h b/spicy/include/ast/types/unit-items/unit-hook.h new file mode 100644 index 000000000..51264df7e --- /dev/null +++ b/spicy/include/ast/types/unit-items/unit-hook.h @@ -0,0 +1,29 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace spicy::type::unit::item { + +/** AST node for a unit hook. */ +class UnitHook : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + UnitHook(ID id, Hook hook, Meta m = Meta()) : NodeBase(nodes(std::move(id), std::move(hook)), std::move(m)) {} + + auto id() const { return child(0); } + auto hook() const { return child(1); } + + bool operator==(const UnitHook& other) const { return id() == other.id() && hook() == other.hook(); } + + // Unit field interface + Type itemType() const { return hook().type(); } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{}; } +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit-items/unresolved-field.h b/spicy/include/ast/types/unit-items/unresolved-field.h new file mode 100644 index 000000000..a136c51a5 --- /dev/null +++ b/spicy/include/ast/types/unit-items/unresolved-field.h @@ -0,0 +1,112 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace spicy::type::unit::item { + +/** + * AST node for a unit field with its type determined by a not yet resolved + * ID. The ID may refer to either a type or an ctor. + */ +class UnresolvedField : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + UnresolvedField(const std::optional& id, Type type, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, + std::vector hooks = {}, Meta m = Meta()) + + : NodeBase(nodes(std::move(type), id, std::move(repeat), std::move(attrs), std::move(cond), args, sinks, + std::move(hooks)), + std::move(m)), + _engine(e), + _args_start(5), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + UnresolvedField(const std::optional& id, Ctor ctor, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, + std::vector hooks = {}, Meta m = Meta()) + + : NodeBase(nodes(std::move(ctor), id, std::move(repeat), std::move(attrs), std::move(cond), args, sinks, + std::move(hooks)), + std::move(m)), + _engine(e), + _args_start(5), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + UnresolvedField(const std::optional& id, Item item, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, + std::vector hooks = {}, Meta m = Meta()) + + : NodeBase(nodes(std::move(item), id, std::move(repeat), std::move(attrs), std::move(cond), args, sinks, + std::move(hooks)), + std::move(m)), + _engine(e), + _args_start(5), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + UnresolvedField(std::optional id, ID unresolved_id, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, + std::vector hooks = {}, Meta m = Meta()) + : NodeBase(nodes(std::move(unresolved_id), std::move(id), std::move(repeat), std::move(attrs), std::move(cond), + args, sinks, std::move(hooks)), + std::move(m)), + _engine(e), + _args_start(5), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())) {} + + auto fieldID() const { return childs()[1].tryAs(); } + + // Only one of these will have return value. + auto unresolvedID() const { return childs()[0].tryAs(); } + auto type() const { return childs()[0].tryAs(); } + auto ctor() const { return childs()[0].tryAs(); } + auto item() const { return childs()[0].tryAs(); } + + auto repeatCount() const { return childs()[2].tryAs(); } + auto attributes() const { return childs()[3].tryAs(); } + auto condition() const { return childs()[4].tryAs(); } + auto arguments() const { return childs(_args_start, _args_end); } + auto sinks() const { return childs(_sinks_start, _sinks_end); } + auto hooks() const { return childs(_sinks_end, -1); } + Engine engine() const { return _engine; } + + bool operator==(const UnresolvedField& other) const { + return _engine == other._engine && unresolvedID() == other.unresolvedID() && fieldID() == other.fieldID() && + attributes() == other.attributes() && arguments() == other.arguments() && sinks() == other.sinks() && + condition() == other.condition() && hooks() == other.hooks(); + } + + // Unit item interface + Type itemType() const { return hilti::type::unknown; } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{{"engine", to_string(_engine)}}; } + +private: + Engine _engine; + const int _args_start; + const int _args_end; + const int _sinks_start; + const int _sinks_end; +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit-items/variable.h b/spicy/include/ast/types/unit-items/variable.h new file mode 100644 index 000000000..1d3fd109f --- /dev/null +++ b/spicy/include/ast/types/unit-items/variable.h @@ -0,0 +1,44 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +namespace spicy::type::unit::item { + +/** + * AST node for a unit variable. + * + * @note We don't support hooks for variables because we can't reliably + * identify assigments in the generated code. To do that, we'd need to trap + * struct field assignments at the C++ level. + */ +class Variable : public hilti::NodeBase, public spicy::trait::isUnitItem { +public: + Variable(ID id, Type type, const std::optional& default_, std::optional attrs = {}, + Meta m = Meta()) + : NodeBase(nodes(std::move(id), std::move(type), default_, std::move(attrs)), std::move(m)) {} + + auto id() const { return child(0); } + auto default_() const { return childs()[2].tryAs(); } + auto attributes() const { return childs()[3].tryAs(); } + + auto isOptional() const { return AttributeSet::find(attributes(), "&optional"); } + + bool operator==(const Variable& other) const { + return id() == other.id() && itemType() == other.itemType() && default_() == other.default_() && + attributes() == other.attributes(); + } + + // Unit field interface + Type itemType() const { return type::effectiveType(child(1)); } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } + + // Node interface. + auto properties() const { return node::Properties{}; } +}; + +} // namespace spicy::type::unit::item diff --git a/spicy/include/ast/types/unit.h b/spicy/include/ast/types/unit.h new file mode 100644 index 000000000..6df64b467 --- /dev/null +++ b/spicy/include/ast/types/unit.h @@ -0,0 +1,216 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace spicy { + +namespace detail::codegen { +class Grammar; +} // namespace detail::codegen + +namespace type { + +/** AST node for a Spicy unit. */ +class Unit : public hilti::TypeBase, + hilti::type::trait::isAllocable, + hilti::type::trait::isParameterized, + hilti::type::trait::isOnHeap { +public: + Unit(std::vector p, std::vector i, + const std::optional& /* attrs */ = {}, Meta m = Meta()) + : TypeBase(nodes(std::move(p), std::move(i)), std::move(m)) { + _state().flags += type::Flag::NoInheritScope; + } + + Unit(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) { + _state().flags += type::Flag::NoInheritScope; + } + + auto parameters() const { return childsOfType(); } + auto items() const { return childsOfType(); } + + std::optional attributes() const { + auto x = childsOfType(); + if ( x.size() ) + return x[0]; + else + return {}; + } + + auto types() const { + std::vector types; + for ( auto c : childs() ) + types.push_back(c.as().itemType()); + + return types; + } + + /** + * Returns the field of a given name if it exists. This descends + * recursively into childs as well. + */ + std::optional field(const ID& id) const; + +#if 0 + /** Returns all items of a given name. */ + std::vector items(std::string) const; + + /** Returns all items of a given name. */ + std::vector items(ID id) const; +#endif + /** + * Returns all of the unit's items of a particular subtype T. + **/ + template + auto items() const { + std::vector v; + for ( const auto& c : childs() ) { + if ( auto x = c.tryAs() ) + v.push_back(*x); + } + return v; + } + + /** + * Returns the property of a given name if it exists. If it exists more + * than once, it's undefined which one is returned. + */ + std::optional propertyItem(const std::string& name) const { + for ( auto i : items() ) { + if ( i.id() == name ) + return i; + } + + return {}; + } + + /** + * Returns all properties of a given name. + */ + auto propertyItems(const std::string& name) const { + std::vector props; + + for ( const auto& i : items() ) { + if ( i.id() == name ) + props.push_back(i); + } + + return props; + } + + /** + * Returns true if the unit has been declared as publically/externally + * accessible. + */ + auto isPublic() const { return _public; }; + + /** + * Returns true if for this unit the parser generator needs to generate + * code facilitating random access within the data that an instance is + * being parsed from. + * + * \todo Currently this feature gets enabled through an attribute + * (`%random-access`). Eventually we should enable this automatically as + * needed, through static analysis. + */ + bool usesRandomAccess() const { return propertyItem("%random-access").has_value(); } + + /** + * Returns true if this unit type supports connecting to a sink. + * + * \todo Currently we tie this capability to unit types being public, + * which is just a hack until we get something better. Eventually we + * should support this automatically as needed, through static analysis. + */ + bool supportsSinks() const { return isPublic(); } + + /** + * Returns true if this unit type supports connecting a filter. + * + * \todo Currently we tie this capability to unit types being public, + * which is just a hack until we get something better. Eventually we + * should support this automatically as needed, through static analysis. + */ + bool supportsFilters() const { return isPublic(); } + + /** + * Returns true if this unit type can act as a filter. + * + * \todo Currently we tie this capability to unit types being public, + * which is just a hack until we get something better. Eventually we + * should support this automatically as needed, through static analysis. + */ + bool isFilter() const { return propertyItem("%filter").has_value(); } + + /** Returns the grammar associated with the type. It must have been set before through `setGrammar()`. */ + const detail::codegen::Grammar& grammar() const { + assert(_grammar); + return *_grammar; + } + + bool operator==(const Unit& other) const { return typeID() == other.typeID(); } + + // Type interface. + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + // type::trait::Parameterized interface. + auto typeParameters() const { return childs(); } + auto isWildcard() const { return _wildcard; } + + // Node interface. + auto properties() const { return node::Properties{{"public", _public}}; } + + /** + * Copies an existing unit type but changes it ``public`` state. + * + * @param unit original unit type + * @param p true if the copied type is to be public + * @return new type with ``public`` state set as requested + */ + static Unit setPublic(const Unit& unit, bool p) { + auto x = Type(unit)._clone().as(); + x._public = p; + return x; + } + + /** + * Copies an existing unit type, adding further unit items. + * + * @param unit original unit type + * @param items additional items to add + * @return new unit type that includes the additional items + */ + static Unit addItems(const Unit& unit, const std::vector& items) { + auto x = Type(unit)._clone().as(); + for ( auto i : items ) + x.childs().emplace_back(std::move(i)); + + return x; + } + + /** + * Copies an existing unit type, setting its accociated grammar. + * + * @param unit original unit type + * @param g the grammar + * @return new type with the grammar associated + */ + static Unit setGrammar(const Unit& unit, std::shared_ptr g) { + auto x = Type(unit)._clone().as(); + x._grammar = std::move(g); + return x; + } + +private: + bool _public = false; + bool _wildcard = false; + std::shared_ptr _grammar; +}; + + +} // namespace type +} // namespace spicy diff --git a/spicy/include/compiler/detail/codegen/codegen.h b/spicy/include/compiler/detail/codegen/codegen.h new file mode 100644 index 000000000..65187b04a --- /dev/null +++ b/spicy/include/compiler/detail/codegen/codegen.h @@ -0,0 +1,77 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spicy::detail { + +/** + * Spicy's code generator. This is the main internal entry point for + * generating HILTI code from Spicy source code. The Spicy AST reuses many + * HILTI nodes. The code generator's task is to convert a mixed Spicy/HILTI + * AST into a pure HILTI AST. + */ +class CodeGen { +public: + CodeGen(std::shared_ptr context) : _context(std::move(context)), _gb(this), _pb(this) {} + + /** Entry point for transformation from a Spicy AST to a HILTI AST. */ + bool compileModule(hilti::Node* root, bool init, hilti::Unit* u); + + const auto& context() const { return _context; } + const auto& options() const { return _context->options(); } + + hilti::Type compileUnit(const type::Unit& unit, + bool declare_only = true); // Compiles a Unit type into its HILTI struct representation. + + std::optional compileHook( + const type::Unit& unit, const ID& id, + std::optional> field, bool foreach, bool debug, + std::vector params, std::optional body, + std::optional /*priority*/, const hilti::Meta& meta); + + // These must be called only while a module is being compiled. + codegen::ParserBuilder* parserBuilder() { return &_pb; } + codegen::GrammarBuilder* grammarBuilder() { return &_gb; } + hilti::Unit* hiltiUnit() const; // will abort if not compiling a module. + hilti::Module* hiltiModule() const; // will abort if not compiling a module. + NodeRef preserveNode(Expression x); + NodeRef preserveNode(Statement x); + NodeRef preserveNode(Type x); + + const auto& moduleProperties() const { return _properties; } + void recordModuleProperty(hilti::declaration::Property p) { _properties.emplace_back(std::move(p)); } + + void addDeclaration(Declaration d) { + _decls_added.insert(d.id()); + _new_decls.push_back(std::move(d)); + } + + bool haveAddedDeclaration(const ID& id) { return _decls_added.find(id) != _decls_added.end(); } + + +private: + std::shared_ptr _context; + codegen::GrammarBuilder _gb; + codegen::ParserBuilder _pb; + + std::vector _properties; + + hilti::Unit* _hilti_unit = nullptr; + hilti::Node* _root = nullptr; + std::vector _new_decls; + std::unordered_set _decls_added; +}; + +} // namespace spicy::detail diff --git a/spicy/include/compiler/detail/codegen/grammar-builder.h b/spicy/include/compiler/detail/codegen/grammar-builder.h new file mode 100644 index 000000000..490b09644 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/grammar-builder.h @@ -0,0 +1,48 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace spicy::logging::debug { +inline const hilti::logging::DebugStream Grammar("grammar"); +} // namespace spicy::logging::debug + +namespace spicy::detail { + +class CodeGen; + +namespace codegen { + +/** Generates the grammars for all unit types declared in an AST. */ +class GrammarBuilder { +public: + GrammarBuilder(CodeGen* cg) : _cg(cg) {} + + /** + * Generates the grammar for a unit type. The grammar will afterwards be + * available through `grammar()`. + */ + Result run(const type::Unit& unit, Node* node); + + /** + * Returns the grammar for a unit type. The type must have been computed + * through `run()` already, otherwise this will abort That's generally + * done for all AST unit types at the beginning of code generation. + */ + const Grammar& grammar(const type::Unit& unit); + + CodeGen* cg() const { return _cg; } + +private: + CodeGen* _cg; + std::map _grammars; +}; + +} // namespace codegen +} // namespace spicy::detail diff --git a/spicy/include/compiler/detail/codegen/grammar.h b/spicy/include/compiler/detail/codegen/grammar.h new file mode 100644 index 000000000..ebf6c864d --- /dev/null +++ b/spicy/include/compiler/detail/codegen/grammar.h @@ -0,0 +1,132 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace spicy::detail::codegen { + +namespace production { +class Resolved; +using Unresolved = Resolved; +} // namespace production + +/** A Spicy grammar. Each unit is translated into a grammar for parsing. */ +class Grammar { +public: + /** + * Instantiates a new grammar that's initially empty. `setRoot` then + * initializes the grammar with its root production. + * + * @param name name associated with the grammar; must be unique, and is used both for debugging + * and for generating labels during code generation + * @param root top-level root production + * @param l associated location + */ + Grammar(std::string name, Location l = location::None) : _name(std::move(name)), _location(std::move(l)) {} + Grammar() = default; + Grammar(const Grammar&) = default; + Grammar(Grammar&&) = default; + ~Grammar() = default; + Grammar& operator=(Grammar&&) = default; + Grammar& operator=(const Grammar&) = default; + + /** + * Returns the name of the grammar. The name uniquely identifies the + * grammar. + */ + const std::string& name() const { return _name; } + + /** Returns the location associated with the production. */ + const Location& location() const { return _location; } + + /** + * Resolves an previous place-holder production with an actual production. + * Once resolved, parser table construction will use the actual production + * everywhere where the place-holder is referenced. + */ + void resolve(production::Unresolved* r, Production p); + + /** Returns a the actual production a resolved production refers to. */ + const Production& resolved(const production::Resolved& r) const; + + /** + * Sets the root produnction for the grammar. This recursively adds all + * childrens of the root to the grammar, too. The root production cannot + * be changeda anymore once set. + */ + Result setRoot(const Production& p); + + /** + * Freezes the grammar, computes the parsing tables for all previously + * added productions, and then registers the look-ahead sets with all + * `LookAhead` productions. If this method fails, grammar and production + * will be left in an undefined state. + * + * @return error if the parsing tables couldn't be computed (e.g., due to + * ambiguties); the error description will then be describing the issue. + */ + Result finalize(); + + /** Returns the root production, if set already. */ + std::optional root() const { + if ( _root ) + return _prods.at(*_root); + + return {}; + } + + /** + * Returns a closure of all the grammar's productions startin with the + * root. The result maps each production's symbol to the production + * itself. Productions without symbols are not included. + * + * @note will return an empty map until the root production gets set. + */ + const std::map& productions() const { return _prods; } + + /** Returns true if the grammar needs look-ahead for parsing. + * + * @note will always return false until the root production gets set. + */ + bool needsLookAhead() const { return _needs_look_ahead; } + + /** + * Prints the grammar in a (somewhat) human readable form. This is for + * debugging. In *verbose* mode, the grammar and all the internal + * nullable/first/follow tables are printed. + */ + void printTables(std::ostream& out, bool verbose = false); + +private: + void _addProduction(const Production& p); + void _simplify(); + Result _computeTables(); + Result _check(); + std::set _computeClosure(const Production& p); + bool _add(std::map>* tbl, const Production& dst, + const std::set& src, bool changed); + bool _isNullable(std::vector::const_iterator i, std::vector::const_iterator j); + std::set _getFirst(const Production& p); + std::set _getFirstOfRhs(const std::vector& rhs); + std::string _productionLocation(const Production& p) const; + std::vector> _rhss(const Production& p); + + std::string _name; + Location _location; + std::optional _root; + + // Computed by _computeTables() + bool _needs_look_ahead = false; + std::map _prods; + std::map _resolved; + std::vector _nterms; + std::map _nullable; + std::map> _first; + std::map> _follow; +}; + +} // namespace spicy::detail::codegen diff --git a/spicy/include/compiler/detail/codegen/parser-builder.h b/spicy/include/compiler/detail/codegen/parser-builder.h new file mode 100644 index 000000000..831782838 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/parser-builder.h @@ -0,0 +1,409 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +namespace hilti::type { +class Struct; +} // namespace hilti::type +namespace hilti::builder { +class Builder; +} // namespace hilti::builder + +namespace spicy::detail { + +class CodeGen; + +namespace codegen { + +struct ProductionVisitor; + +/** + * Conveys to the parsing logic for literals what the caller wants them to + * do. This is needed to for doing look-ahead parsing, and hence not relevant + * for fields that aren't literals. + */ +enum class LiteralMode { + /** Normal parsing: parse field and raise parse error if not possible. */ + Default, + + /** + * Try to parse the field, but do not raise an error if it fails. If + * it works, move cur as normal; if it fails, set cur to end. + */ + Try, +}; + +namespace detail { +constexpr util::enum_::Value literal_modes[] = {{LiteralMode::Default, "default"}, + {LiteralMode::Try, "try"}}; +} // namespace detail + +constexpr auto to_string(LiteralMode cc) { return util::enum_::to_string(cc, detail::literal_modes); } + +namespace look_ahead { + +/** Type for storing a look-ahead ID. */ +extern const hilti::Type Type; + +/** + * Expression representing "no look-ahead" symbol through a zero, a value + * different from any look-ahead ID. With 0 being the value, it can be used + * in a boolean context to evaluate to false. + */ +extern const hilti::Expression None; + +/** + * Expression representing a virtual "end-of-data" symbol through a value + * different from any look-ahead ID (and also from `None`). + */ +extern const hilti::Expression Eod; + +} // namespace look_ahead + +/** + * Maintains access to parser state during code generation. The generated + * parsing code needs to carry various pieces of state through the logic + * (e.g., the curren input data). This struct records the expressions that + * arte holding the current state variables. To change same state (e.g., to + * temporarily parse different input) one typically creates a copy of the + * current struct instance and then pushed that onto parser generator's state + * stack. To change it back, one pops that struct from the stack. + */ +struct ParserState { + ParserState(const type::Unit& unit, const Grammar& grammar, Expression data, Expression cur); + ParserState(const ParserState& other) = default; + ParserState(ParserState&& other) = default; + ParserState& operator=(const ParserState& other) = default; + ParserState& operator=(ParserState&& other) = default; + ~ParserState() = default; + + /** + * Generates code that prints a representation of the state to the + * `spicy-verbose` debug stream. + * + * @param block bock to add the generated code to + */ + void printDebug(const std::shared_ptr& b) const; + + /** Unit type that's currently being compiled. */ + std::reference_wrapper unit; + + /** Type name of unit type tha's currently being compiled. */ + ID unit_id; + + /** True if the current grammar needs look-ahead tracking. */ + bool needs_look_ahead; + + /**< Expression referencing the current parse object. */ + Expression self; + + /**< Expression referencing the stream instance we're working on. */ + Expression data; + + /**< Expression referencing the current view inside 'data'. */ + Expression cur; + + /**< If set, expression referencing a new `cur` to set after parsin the current rule. */ + std::optional ncur; + + /** + * Boolean expression indicating whether the input data can be trimmed + * once consumed. + */ + Expression trim; + + /** + * Expression with the current look-ahead symbol, or `look_ahead::None` + * if none. Look ahead-symbols are of type `look_ahead::Type`. + */ + Expression lahead = look_ahead::None; + + /** + * Expression with a iterator pointing to the end of the current + * look-ahead symbol. Only well-defined if *lahead* is set. + */ + Expression lahead_end; + + /** Mode for parsing literals. */ + LiteralMode literal_mode = LiteralMode::Default; +}; + +/** Generates the parsing logic for a unit type. */ +class ParserBuilder { +public: + ParserBuilder(CodeGen* cg) : _cg(cg) {} + + /** + * Pushes new parsing state onto the stack. The new state will then be + * used by any subsequent code generation. + */ + void pushState(ParserState p) { _states.push_back(std::move(p)); } + + /** + * Remove the top element from the parsing state stack, switching back to + * the previous state. + */ + void popState() { _states.pop_back(); } + + /** Returns the current parsing state. */ + ParserState state() const { return _states.back(); } + + /** + * Returns an expression referencing the 1st version of publically + * visible method that implements a unit's parsing logic, to be called + * from a host application. This version returns just the data remaining + * after parsing the unit. + */ + Expression parseMethodExternalOverload1(const type::Unit& t); + + /** + * Returns an expression referencing the 2nd version of publically + * visible method that implements a unit's parsing logic, to be called + * from a host application. This version returns just the parsed object + * plus the data remaining after parsing the unit. + */ + Expression parseMethodExternalOverload2(const type::Unit& t); + + /** + * Adds a unit's external parsing methods to the HILTI struct + * corresponding to the parse object. Returns the modified type. + */ + hilti::type::Struct addParserMethods(hilti::type::Struct s, const type::Unit& t, bool declare_only); + + /** Returns statement builder currently being active. */ + auto builder() { return _builders.back(); } + + /** Activates a statement builder for subsequent code. */ + auto pushBuilder(std::shared_ptr b) { + _builders.emplace_back(b); + return b; + } + + /** Creates a new statement builder and activates it for subsequent code. */ + std::shared_ptr pushBuilder(); + + /** Deactivates the most recent statement builder. */ + auto popBuilder() { + auto x = _builders.back(); + _builders.pop_back(); + return x; + } + + /** An object whose destructor pops the most recent statement builder. */ + struct ScopeGuard { + ScopeGuard(ParserBuilder* self) { this->self = self; } + ScopeGuard(ScopeGuard&&) = default; + ~ScopeGuard() { self->popBuilder(); } + + ScopeGuard() = delete; + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + ScopeGuard& operator=(ScopeGuard&&) noexcept = delete; + + ParserBuilder* self; + }; + + /** Returns an object whose destructor pops the most recent statement builder. */ + ScopeGuard makeScopeGuard() { return ScopeGuard(this); } + + /** Activates a statement builder for subsequent code. */ + auto pushBuilder(std::shared_ptr b, const std::function& func) { + pushBuilder(b); + func(); + popBuilder(); + return b; + } + + /** Generates code that parses an instance of a specific type. */ + Expression parseType(const Type& t, const std::optional& field, + const std::optional& dst); + + /** Generates code that parses an instance of a specific type into an expression yielding a `Result` of `t`. */ + Expression parseTypeTry(const Type& t, const std::optional& field, + const std::optional& dst); + + /** Returns the type for a `parse_stageX` unit method. */ + hilti::type::Function parseMethodFunctionType(std::optional addl_param = {}, + const Meta& m = {}); + + /** + * Generates code that parses an instance of a specific literal, meaning + * it matches the value against the input. In literal mode `Default`, + * returns the parsed value and advances `cur`, consuming the current + * look-ahead symbol if any, and throwing a parse error if it couldn't + * parse it. In literal mode `Try`, returns an iterator pointing right + * after the parsed literal, with an iterator equal to `begin(cur)` + * meaning no match (and does not advance `cur`). + */ + Expression parseLiteral(const Production& p, const std::optional& dst); + + /** + * Generates code that ensures that a mininum amount of data is available + * for parsing. The generated code will wait until enough data becomes + * available before proceeding. It will abort parsing if end-of-data is + * reached before that. + * + * @param min unsigned integer expression specifying the requited number + * of bytes. + * @param error_msg message to report with parse error if end-of-data is reached + * @param location location assocated with the operation. + */ + void waitForInput(const Expression& min, const std::string& error_msg, const Meta& location); + + /** + * Generates code that ensures that either a mininum amount of data is + * available for parsing, or end-of-data is reached. The generated code + * will wait until either happens. + * + * @param min unsigned integer expression specifying the requited number of + * bytes. + * + * @return A boolean expression that's true if sufficient bytes are + * available, and false if end-of-data has been reached. + */ + Expression waitForInputOrEod(const Expression& min); + + /** + * Generates code that waits for more input. If end-of-data is reached + * before additional input becomes available, it triggers a parse error. + * + * @param error_msg message to report with parse error if end-of-data is reached + * @param location location associated with the operation + */ + void waitForInput(const std::string& error_msg, const Meta& location); + + /** + * Generates code that waits for either more input becoming available or + * end of data being reached.. + * + * @param location location associated with the operation + * @return A boolean expression that's true if more bytes have become + * available, and false if end-of-data has been reached. + */ + Expression waitForInputOrEod(); + + /** + * Generates code that waits for end-of-data to be obtained (but not + * necessarily reached). + */ + void waitForEod(); + + /** Returns a boolean expression that's true if EOD has been reached. */ + Expression atEod(); + + /** + * Generates code that advances the current view to a new start position. + * This implicitly calls advancedInput() afterwards. + * + * @param i expression that's either the number of bytes to move ahead, + * a stream iterator to move to, or a new stream view to use from now on. + */ + void advanceInput(const Expression& i); + + /** + * Generates code that sets the current view. + * + * @param i expression that's the new view to use. + */ + void setInput(const Expression& i); + + /** + * Generates code that saves the current parsing position inside the + * current parse object. This only has an effect for unit types that + * support random access, it's a no-op for others. + */ + void saveParsePosition(); + + /** Inserts code that needs to run before a user hook gets executed. */ + void beforeHook(); + + /** Inserts code that needs to run after a user hook was executed. */ + void afterHook(); + + /** + * Generates code that consumes the current look-ahead symbol. It clears + * `lahead`, move `cur` to `lahead_end`, and optionally stores the + * look-ahead token itself into a custom destination. + * + * @param dst A RHS expression of type bytes to store the token into. + */ + void consumeLookAhead(std::optional dst = {}); + + /** Generates code that triggers a parse error exeception. */ + void parseError(const std::string& error_msg, const Meta& location); + + /** Generates code that triggers a parse error exeception. */ + void parseError(const Expression& error_msg, const Meta& location); + + /** Called when a field has been updated. */ + void newValueForField(const type::unit::item::Field& field, const Expression& value); + + /** + * Signal that new values for fields are reported through custom logic, + * disable default reporting for current field. + */ + void enableDefaultNewValueForField(bool enable) { _report_new_value_for_field = enable; }; + + /** + * Returns true if default reporting of new value is enabled for the + * current field. + */ + bool isEnabledDefaultNewValueForField() { return _report_new_value_for_field; } + + /** + * Called when a container item has been parsed. Returns a boolean + * expression that true if container parsing is to continue. + */ + Expression newContainerItem(const type::unit::item::Field& field, const Expression& self, const Expression& item); + + /** + * Trims the input's beginning to the current parsing position, + * discarding all data preceeding it. By default, this does not do + * anything if the current parsing state does not allow trimming. + * + * @param force always trim, independent of the parsing state's trimming state + */ + void trimInput(bool force = false); + + /** + * Generates code that initializes a unit instance just before parsing + * begins. + * + * @param l location to assocatiate with the generated code + */ + void initializeUnit(const Location& l); + + /** + * Generates code that cleans up a unit instances after parsing has + * finished, normally or abnormally. + * + * @param success true if parsing was succesful, false if an error occured. + * @param l location to assocatiate with the generated code + */ + void finalizeUnit(bool success, const Location& l); + + CodeGen* cg() const { return _cg; } + const std::shared_ptr& context() const; + const hilti::Options& options() const; + +private: + friend struct spicy::detail::codegen::ProductionVisitor; + CodeGen* _cg; + + Expression _parseType(const Type& t, const std::optional& field, + const std::optional& dst, bool is_try); + + std::vector _states; + std::vector> _builders; + std::map _functions; + bool _report_new_value_for_field = true; +}; + +} // namespace codegen +} // namespace spicy::detail diff --git a/spicy/include/compiler/detail/codegen/production.api b/spicy/include/compiler/detail/codegen/production.api new file mode 100644 index 000000000..d32d3cceb --- /dev/null +++ b/spicy/include/compiler/detail/codegen/production.api @@ -0,0 +1,142 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +class Production_(trait::isProduction) { + trait isLiteral() from spicy::trait::isLiteral; + trait isNonTerminal() from spicy::trait::isNonTerminal; + trait isTerminal() from spicy::trait::isTerminal; + + /** + * Returns true if it's possible to derive the production to an Epsilon + * production. Note that it doesn't *always* need to do so, just one + * possible derivation is sufficient. + */ + bool nullable() const; + + /** + * Returns true if running out of data while parsing this production + * should not be considered an error. + */ + bool eodOk() const; + + /** + * Returns true if this production does not recursively contain other + * productions. + */ + bool atomic() const; + + /** + * Returns true if this production in principle supports being parsed in + * synchronization mode (i.e., after a parser error; or in DPD mode, it + * knows how to find a subsequent point in the input stream where it can + * continue normally). This is independent of whether the user has + * requested to activate that support. + */ + bool supportsSynchronize() const; + + /** Returns any type associated with this production. */ + std::optional type() const; + + /** + * Returns a readable representation of the production, suitable to + * include in error message and debugging output. This should usually not + * be called directly; convert the production into a string instead, + * which will incorporate the output of this method, but augment it + * further. + */ + std::string render() const; + + /** + * For literals, returns the expression associated with it. + */ + Expression expression() const if spicy::trait::isLiteral; + + /** + * Returns a ID for this literal that's guaranteed to be globally + * unique for the literal's value, including across grmmars. + */ + int64_t tokenID() const if spicy::trait::isLiteral; + + /** + * Returns a list of RHS alternatives for this production. Each RHS is + * itself a list of Production instances. + */ + std::vector> rhss() const if spicy::trait::isNonTerminal; + + /** + * Returns true if this production is flagged by the user as ok to be + * parsed in synchronization mode through the `&synchronize` keyword. + * This is independent of whether the production actually supports that. + * This is implemented for all productions in `ProductionBase`. + */ + bool maySynchronize() const; + + /** + * Returns the location associated with the production, or Location::None + * if none. This is implemented for all productions in `ProductionBase`. + * + */ + const Location& location() const; + + /** Returns the symbol asssociated with the production. This is + implemented for all productions in `ProductionBase`. */ + const std::string& symbol() const; + + /** + * Renames the production. This is implemented for all productions in + * `ProductionBase`. + */ + void setSymbol(const std::string& s); + + /** + * For terminals, returns the filter function associated with it, if any. + * This is implemented for all productions in `ProductionBase`. + */ + std::optional filter() const if spicy::trait::isTerminal; + + /** + * For terminals, associates a filter function with it. The filter + * function will be called when a value has been parsed for the terminal. + * It must return a value to use instead of the parsed value. This is + * implemented for all productions in `ProductionBase`. + */ + void setFilter(const Expression& filter) if spicy::trait::isTerminal; + + /** + * For terminals, returns the sink associated with it, if any. This is + * implemented for all productions in `ProductionBase`. + */ + std::optional sink() const if spicy::trait::isTerminal; + + /** + * For terminals, associates a sink with it. Any parsed data will be + * forwarded to the sink. This is implemented for all productions in + * `ProductionBase`. + */ + void setSink(const Expression& sink) if spicy::trait::isTerminal; + + /** + * Returns access to the production meta data. The meta data is filled as + * grammar and parser are being built. This is implemented for all + * productions in `ProductionBase`. + */ + const production::Meta& meta() const; + + /** + * Sets the production meta data. The meta data is filled as + * grammar and parser are being built. This is implemented for all + * productions in `ProductionBase`. + */ + void setMeta(production::Meta m); + + /** + * Returns the internal meta instance the production is using. For + * internal infrastructure use only. + */ + std::shared_ptr _metaInstance() const; + + /** + * Sets the internal meta instance the production is using. For internal + * infrastructure use only. + */ + void _setMetaInstance(std::shared_ptr m); +}; diff --git a/spicy/include/compiler/detail/codegen/production.h b/spicy/include/compiler/detail/codegen/production.h new file mode 100644 index 000000000..3eae701ab --- /dev/null +++ b/spicy/include/compiler/detail/codegen/production.h @@ -0,0 +1,228 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +namespace spicy { + +using hilti::Location; +using hilti::Nothing; +using hilti::Result; +namespace location = hilti::location; + +namespace trait { +class isProduction {}; +class isTerminal {}; +class isNonTerminal {}; +class isLiteral : public isTerminal {}; +} // namespace trait +} // namespace spicy + +#include +#include + +namespace spicy::detail::codegen { + +class Production; + +namespace production { + +/* Meta data that the parser builder associates with a production. */ +class Meta { +public: + /** Returns a unit field associated with the production, if set. */ + auto field() const { return _value(_field); } + + /** + * Returns true if there's a field associated with this production, and + * the production is the top-level entry point for parsing that field + * (vs. being a nested production further down in the parse tree). + */ + bool isFieldProduction() const { return _field && _is_field_production; } + + /** + * If this production corresponds to a container's item field, this + * returns the container (once set). + */ + auto container() const { return _value(_container); } + + /** + * If the production corresponds to a for-each hook, this returns the + * corresponding field (once set). + */ + auto forEach() const { return _value(_for_each); } + + void setField(const NodeRef& n, bool is_field_production) { + assert(n); + _is_field_production = is_field_production; + _field = n; + } + + void setContainer(const NodeRef& n) { + assert(n); + _container = n; + } + + void setForEach(const NodeRef& n) { + assert(n); + _for_each = n; + } + + NodeRef fieldRef() const { return NodeRef(_field); } + NodeRef containerRef() const { return NodeRef(_container); } + +private: + template + std::optional _value(const NodeRef& n) const { + if ( n ) + return n->as(); + + return {}; + } + + bool _is_field_production = false; + NodeRef _field; + NodeRef _container; + NodeRef _for_each; +}; + +#include + +/** + * Returns a readable representation of a production for diagnostics. + */ +extern std::string to_string(const Production& p); + +/** + * Returns a unqiue (and stable) token ID for a given string + * representatin of a production. + */ +extern int64_t tokenID(const std::string& p); + +} // namespace production + +/** + * A single production inside a grammar. This is a type-erased class that + * wraps all types of productions. + * + * @note Do not derive from this class. Implement the `Production` interface + * instead. + */ +class Production final : public production::Production_ { +public: + /** Constructs a production from an instance of a class implementing the `Production` interface. */ + template::value>* = nullptr> + Production(T t) : codegen::production::Production_(std::move(t)) {} + + ~Production() final = default; + Production() = default; + Production(const Production&) = default; + Production(Production&&) noexcept = default; + Production& operator=(const Production&) = default; + Production& operator=(Production&&) = default; + + /** + * Returns a readable representation of the production for diagnostics. + */ + explicit operator std::string() const { return to_string(*this); } +}; + +/** Renders a production for diagnostics. */ +inline std::ostream& operator<<(std::ostream& out, const Production& p) { + out << to_string(p); + return out; +} + +/** Returns true if the two production's symbols match. */ +inline bool operator==(const Production& p1, const Production& p2) { + if ( &p1 == &p2 ) + return true; + + return p1.symbol() == p2.symbol(); +} + +/** Sorts by the productions' symbols. */ +inline bool operator<(const Production& p1, const Production& p2) { return p1.symbol() < p2.symbol(); } + +namespace production { +/** + * Returns if inside a list of production list, at least one is nullable. + * Also returns true if the list of lists is empty to begin with. + */ +extern bool nullable(const std::vector>& rhss); + +} // namespace production + +/** + * Common base class for classes implementing the `Production` interface. The + * base implements a number of the interface methods with standard versions + * shared across all nodes. + */ +class ProductionBase : public trait::isProduction { +public: + /** + * Constructor. + * + * @param symbol symbol associated with the production; the symbol must + * be unique within the grammar the production is (or will + * be) part of (unless it's empty). + * @param m meta data associated with the + * @param l location associated with the production + */ + ProductionBase(std::string symbol, Location l = location::None) + : _symbol(std::move(symbol)), _location(std::move(l)), _meta(new production::Meta()) {} + + /** Returns true if the production's associated field has a `&size` attribute. */ + bool hasSize() const { return meta().field() && AttributeSet::find(meta().field()->attributes(), "&size"); } + + /** Implements the `Production` interface. */ + bool maySynchronize() const { + return meta().field() && AttributeSet::find(meta().field()->attributes(), "&synchronize"); + } + + /** Implements the `Production` interface. */ + const Location& location() const { return _location; } + + /** Implements the `Production` interface. */ + const std::string& symbol() const { return _symbol; } + + /** Implements the `Production` interface. */ + void setSymbol(const std::string& s) { _symbol = s; } + + /** Implements the `Production` interface. */ + std::optional filter() const { return _filter; } + + /** Implements the `Production` interface. */ + void setFilter(const Expression& filter) { _filter = filter; } + + /** Implements the `Production` interface. */ + std::optional sink() const { return _sink; } + + /** Implements the `Production` interface. */ + void setSink(const Expression& sink) { _sink = sink; } + + /** Implements the `Production` interface. */ + const production::Meta& meta() const { return *_meta; } + + /** Implements the `Production` interface. */ + void setMeta(production::Meta m) { *_meta = std::move(m); } + + /** Implements the `Production` interface. */ + std::shared_ptr _metaInstance() const { return _meta; } + + void _setMetaInstance(std::shared_ptr m) { _meta = std::move(m); } + +private: + std::string _symbol; + Location _location; + std::optional _filter; + std::optional _sink; + std::shared_ptr _meta; +}; + +} // namespace spicy::detail::codegen diff --git a/spicy/include/compiler/detail/codegen/productions.decl b/spicy/include/compiler/detail/codegen/productions.decl new file mode 100644 index 000000000..e6dc603e4 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions.decl @@ -0,0 +1,17 @@ +trait spicy::detail::codegen::Production isProduction + +spicy::detail::codegen::production::Boolean : isProduction +spicy::detail::codegen::production::ByteBlock : isProduction +spicy::detail::codegen::production::Counter : isProduction +spicy::detail::codegen::production::Ctor : isProduction +spicy::detail::codegen::production::Enclosure : isProduction +spicy::detail::codegen::production::Epsilon : isProduction +spicy::detail::codegen::production::ForEach : isProduction +spicy::detail::codegen::production::LookAhead : isProduction +spicy::detail::codegen::production::Sequence : isProduction +spicy::detail::codegen::production::Switch : isProduction +spicy::detail::codegen::production::TypeLiteral : isProduction +spicy::detail::codegen::production::Unit : isProduction +spicy::detail::codegen::production::Resolved : isProduction +spicy::detail::codegen::production::Variable : isProduction +spicy::detail::codegen::production::While : isProduction diff --git a/spicy/include/compiler/detail/codegen/productions/all.h b/spicy/include/compiler/detail/codegen/productions/all.h new file mode 100644 index 000000000..c9fc985f6 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/all.h @@ -0,0 +1,19 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/spicy/include/compiler/detail/codegen/productions/boolean.h b/spicy/include/compiler/detail/codegen/productions/boolean.h new file mode 100644 index 000000000..11c658abf --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/boolean.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A pair of alternatives between which we decide based on a boolean + * expression. + */ +class Boolean : public ProductionBase, public spicy::trait::isNonTerminal { +public: + Boolean(const std::string& symbol, Expression e, Production alt1, Production alt2, + const Location& l = location::None) + : ProductionBase(symbol, l), + _expression(std::move(e)), + _alternatives(std::make_pair(std::move(alt1), std::move(alt2))) {} + + const Expression& expression() const { return _expression; } + const std::pair& alternatives() const { return _alternatives; } + + // Production API + std::vector> rhss() const { return {{_alternatives.first}, {_alternatives.second}}; } + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { + // Always false. If one of the branches is ok with no data, it will + // indicate so itself. + return false; + } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize(); } + std::string render() const { + return util::fmt("true: %s / false: %s", _alternatives.first.symbol(), _alternatives.second.symbol()); + } + +private: + Expression _expression; + std::pair _alternatives; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/byte-block.h b/spicy/include/compiler/detail/codegen/productions/byte-block.h new file mode 100644 index 000000000..ea934f285 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/byte-block.h @@ -0,0 +1,38 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A production that parses a byte block of a given length with another production. + * + * TODO: Not currently used/implemented. Do we need this? (Looks like the old + * prototype also didn't use it.) + */ +class ByteBlock : public ProductionBase, public spicy::trait::isNonTerminal { +public: + ByteBlock(const std::string& symbol, Expression e, Production body, const Location& l = location::None) + : ProductionBase(symbol, l), _expression(std::move(e)), _body(std::move(body)) {} + + const Expression& expression() const { return _expression; } + const Production& body() const { return _body; } + + // Production API + std::vector> rhss() const { return {{_body}}; } + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize(); } + std::string render() const { return util::fmt("byte-block(%s): %s", _expression, _body.symbol()); } + +private: + Expression _expression; + Production _body; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/counter.h b/spicy/include/compiler/detail/codegen/productions/counter.h new file mode 100644 index 000000000..59b131137 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/counter.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A production executing a certain number of times as given by an integer + * expression. + */ +class Counter : public ProductionBase, public spicy::trait::isNonTerminal { +public: + Counter(const std::string& symbol, Expression e, Production body, const Location& l = location::None) + : ProductionBase(symbol, l), _expression(std::move(e)), _body(std::move(body)) {} + + const Expression& expression() const { return _expression; } + const Production& body() const { return _body; } + + // Production API + std::vector> rhss() const { return {{_body}}; } + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize(); } + std::string render() const { return util::fmt("counter(%s): %s", _expression, _body.symbol()); } + +private: + Expression _expression; + Production _body; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/ctor.h b/spicy/include/compiler/detail/codegen/productions/ctor.h new file mode 100644 index 000000000..42179cdd3 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/ctor.h @@ -0,0 +1,30 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** A literal represented by a ctor. */ +class Ctor : public ProductionBase, public spicy::trait::isLiteral { +public: + Ctor(const std::string& symbol, spicy::Ctor ctor, const Location& l = location::None) + : ProductionBase(symbol, l), _ctor(std::move(ctor)) {} + + spicy::Ctor ctor() const { return _ctor; }; + Expression expression() const { return hilti::expression::Ctor(_ctor); } + std::optional type() const { return _ctor.type(); } + bool nullable() const { return false; } + bool eodOk() const { return nullable(); } + bool atomic() const { return true; } + bool supportsSynchronize() const { return hasSize() && maySynchronize(); } + int64_t tokenID() const { return production::tokenID(util::fmt("%s|%s", _ctor, _ctor.type())); } + std::string render() const { return util::fmt("%s (%s)", _ctor, _ctor.type()); } + +public: + spicy::Ctor _ctor; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/enclosure.h b/spicy/include/compiler/detail/codegen/productions/enclosure.h new file mode 100644 index 000000000..1367b6104 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/enclosure.h @@ -0,0 +1,35 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A wrapper that forwards directly to another grammar (within the same unit + * type). This can be used to hook into starting/finishing parsing for that + * other grammar. + */ +class Enclosure : public ProductionBase, public spicy::trait::isNonTerminal { +public: + Enclosure(const std::string& symbol, Production child, const Location& l = location::None) + : ProductionBase(symbol, l), _child(std::move(child)) {} + + const Production& child() const { return _child; } + + // Production API + std::vector> rhss() const { return {{_child}}; }; + std::optional type() const { return _child.type(); } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize() || _child.supportsSynchronize(); } + std::string render() const { return _child.symbol(); } + +public: + Production _child; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/epsilon.h b/spicy/include/compiler/detail/codegen/productions/epsilon.h new file mode 100644 index 000000000..faa455158 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/epsilon.h @@ -0,0 +1,22 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy::detail::codegen::production { + +/** Empty epsilon production. */ +class Epsilon : public ProductionBase, spicy::trait::isTerminal { +public: + Epsilon(Location l = location::None) : ProductionBase("", std::move(l)) {} + + bool nullable() const { return true; } + bool eodOk() const { return nullable(); } + bool atomic() const { return true; } + bool supportsSynchronize() const { return false; } + std::optional type() const { return {}; } + std::string render() const { return "()"; } +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/for-each.h b/spicy/include/compiler/detail/codegen/productions/for-each.h new file mode 100644 index 000000000..d48641ef3 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/for-each.h @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** A production executing until interrupted by a foreach hook. */ +class ForEach : public ProductionBase, public spicy::trait::isNonTerminal { +public: + ForEach(const std::string& symbol, Production body, bool eod_ok, const Location& l = location::None) + : ProductionBase(symbol, l), _body(std::move(body)), _eod_ok(eod_ok) {} + + const Production& body() const { return _body; } + + // Production API + std::vector> rhss() const { return {{_body}}; } + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return _eod_ok ? _eod_ok : nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize() || _body.supportsSynchronize(); } + std::string render() const { return util::fmt("foreach: %s", _body.symbol()); } + +private: + Production _body; + bool _eod_ok; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/look-ahead.h b/spicy/include/compiler/detail/codegen/productions/look-ahead.h new file mode 100644 index 000000000..59f844bbf --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/look-ahead.h @@ -0,0 +1,67 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +namespace look_ahead { +enum class Default { First, Second, None }; +} // namespace look_ahead + +/** + * A pair of alternatives between which we can decide with one token of + * look-ahead. + */ +class LookAhead : public ProductionBase, public spicy::trait::isNonTerminal { +public: + LookAhead(const std::string& symbol, Production alt1, Production alt2, look_ahead::Default def, + const Location& l = location::None) + : ProductionBase(symbol, l), + _alternatives(std::make_pair(std::move(alt1), std::move(alt2))), + _default(def), + _lahs(new std::pair, std::set>) {} + + LookAhead(const std::string& symbol, Production alt1, Production alt2, const Location& l = location::None) + : LookAhead(symbol, std::move(alt1), std::move(alt2), look_ahead::Default::None, l) {} + + /** Returns the two alternatives. */ + const std::pair& alternatives() const { return _alternatives; } + + /** Returns what's the default alternative. */ + look_ahead::Default default_() const { return _default; } + + /** + * Returns the look-aheads for the two alternatives. This function will + * return a valid value only after the instance has been added to a + * `Grammar`, as that's when the look-aheads are computed. + */ + const std::pair, std::set>& lookAheads() const { return *_lahs; } + + /** + * Sets the look-aheads for the two alternatives. This function is called + * from a `Grammar` when the production is added to it. + */ + void setLookAheads(std::pair, std::set>&& lahs) { *_lahs = std::move(lahs); } + + // Production API + std::vector> rhss() const { return {{_alternatives.first}, {_alternatives.second}}; } + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const; + std::string render() const; + +private: + std::pair _alternatives; + look_ahead::Default _default; + + // This violates value-semantics but we need to share updates with + // existing copies of the production + std::shared_ptr, std::set>> _lahs; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/resolved.h b/spicy/include/compiler/detail/codegen/productions/resolved.h new file mode 100644 index 000000000..72bdda135 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/resolved.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy::detail::codegen { +class Grammar; +} // namespace spicy::detail::codegen + +namespace spicy::detail::codegen::production { + +/* + * Place-holder production that's resolved through a `Grammar` later. This + * can used to be create to self-recursive grammars. + * + * @note This option doesn't actually implement most of the `Production` API + * (meaniningfully). + */ +class Resolved : public ProductionBase { +public: + Resolved(const Location& l = location::None) + : ProductionBase("", l), + _symbol(std::make_shared("")), + _rsymbol(util::fmt("ref:%d", ++_cnt)) {} + std::string render() const { return symbol(); } + + const std::string& symbol() const { return *_symbol; } + const std::string& referencedSymbol() const { return _rsymbol; } + + void resolve(const std::string& symbol) { *_symbol = symbol; } + + // Production API methods are meaningless for this one. + bool nullable() const { return false; } + bool eodOk() const { return false; } + bool atomic() const { return true; } + bool supportsSynchronize() const { return false; } + std::optional type() const { return {}; } + +public: + std::shared_ptr _symbol; + std::string _rsymbol; + + inline static int _cnt = 0; +}; + +// Alias the name for clarity. The idea is that initiallu one creates +// `Unresolved` instances. Once they have been resolved, one then operates on +// `Resolved` instances. Internally, however, the two are the same. +using Unresolved = Resolved; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/sequence.h b/spicy/include/compiler/detail/codegen/productions/sequence.h new file mode 100644 index 000000000..de94c032e --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/sequence.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A wrapper that forwards directly to another grammar (within the same unit + * type). This can be used to hook into starting/finishing parsing for that + * other grammar. + */ +class Sequence : public ProductionBase, public spicy::trait::isNonTerminal { +public: + Sequence(const std::string& symbol, std::vector prods, const Location& l = location::None) + : ProductionBase(symbol, l), _prods(std::move(prods)) {} + + const std::vector& sequence() const { return _prods; } + void add(Production p) { _prods.push_back(std::move(p)); } + + // Production API + std::vector> rhss() const { return {_prods}; }; + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize() || (_prods.size() && _prods.front().supportsSynchronize()); } + std::string render() const { + return util::join(util::transform(_prods, [](const auto& p) { return p.symbol(); }), " "); + } + +public: + std::vector _prods; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/switch.h b/spicy/include/compiler/detail/codegen/productions/switch.h new file mode 100644 index 000000000..8f0f7866b --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/switch.h @@ -0,0 +1,48 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * Production that decides between alternatives based on which value out of a + * set of options a given expression matches; plus an optional default if none matches. + */ +class Switch : public ProductionBase, public spicy::trait::isNonTerminal { +public: + using Cases = std::vector, Production>>; + + Switch(const std::string& symbol, Expression expr, Cases cases, std::optional default_, + const Location& l = location::None) + : ProductionBase(symbol, l), + _expression(std::move(expr)), + _cases(std::move(cases)), + _default(std::move(default_)) {} + + const Expression& expression() const { return _expression; } + const Cases& cases() const { return _cases; } + const std::optional& default_() const { return _default; } + + // Production API + std::vector> rhss() const; + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { + // Always false. If one of the branches is ok with no data, it will + // indicate so itself. + return false; + } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize(); } + std::string render() const; + +private: + Expression _expression; + Cases _cases; + std::optional _default; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/type-literal.h b/spicy/include/compiler/detail/codegen/productions/type-literal.h new file mode 100644 index 000000000..423c0d723 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/type-literal.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A literal represented by a type. A type can only be used as literals if + * the parsing can for tell for sure that an instance of it must be coming + * up. This is, e.g., the case for embedded objects. + */ +class TypeLiteral : public ProductionBase, public spicy::trait::isLiteral { +public: + TypeLiteral(const std::string& symbol, spicy::Type type, const Location& l = location::None) + : ProductionBase(symbol, l), _type(std::move(type)) {} + + Expression expression() const { return hilti::expression::Type_(_type); } + std::optional type() const { return _type; } + bool nullable() const { return false; } + bool eodOk() const { return nullable(); } + bool atomic() const { return true; } + bool supportsSynchronize() const { return hasSize() && maySynchronize(); } + int64_t tokenID() const { return production::tokenID(util::fmt("%s", _type)); } + std::string render() const { return util::fmt("%s", _type); } + +private: + spicy::Type _type; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/unit.h b/spicy/include/compiler/detail/codegen/productions/unit.h new file mode 100644 index 000000000..fe9b54d84 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/unit.h @@ -0,0 +1,47 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen { +class Grammar; +} // namespace spicy::detail::codegen + +namespace spicy::detail::codegen::production { + +/** + * A type described by another grammar from an independent `type::Unit` type. + */ +class Unit : public ProductionBase, public spicy::trait::isNonTerminal { +public: + Unit(const std::string& symbol, type::Unit type, std::vector args, std::vector fields, + const Location& l = location::None) + : ProductionBase(symbol, l), _type(std::move(type)), _args(std::move(args)), _fields(std::move(fields)) {} + + const type::Unit& unitType() const { return _type; } + const auto& arguments() const { return _args; } + const auto& fields() const { return _fields; } + + // Production API + std::vector> rhss() const { return {_fields}; }; + std::optional type() const { return spicy::Type(_type); } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { + return hasSize() || (rhss().front().size() && rhss().front().front().supportsSynchronize()) || + _type.propertyItem("synchronize-after") || _type.propertyItem("synchronize-before"); + } + std::string render() const { + return util::join(util::transform(_fields, [](const auto& p) { return p.symbol(); }), " "); + } + +private: + type::Unit _type; + std::vector _args; + std::vector _fields; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/variable.h b/spicy/include/compiler/detail/codegen/productions/variable.h new file mode 100644 index 000000000..63231d177 --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/variable.h @@ -0,0 +1,31 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy::detail::codegen::production { + +/** + * A variable. A variable is a terminal that will be parsed from the input + * stream according to its type, yet is not recognizable as such in advance + * by just looking at the available bytes. If we start parsing, we assume it + * will match (and if not, generate a parse error). + */ +class Variable : public ProductionBase, public spicy::trait::isTerminal { +public: + Variable(const std::string& symbol, spicy::Type type, const Location& l = location::None) + : ProductionBase(symbol, l), _type(std::move(type)) {} + + spicy::Type type() const { return _type; } + bool nullable() const { return false; } + bool eodOk() const { return nullable(); } + bool atomic() const { return true; } + bool supportsSynchronize() const { return hasSize() && maySynchronize(); } + std::string render() const { return util::fmt("%s", _type); } + +private: + spicy::Type _type; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/codegen/productions/while.h b/spicy/include/compiler/detail/codegen/productions/while.h new file mode 100644 index 000000000..d5598087e --- /dev/null +++ b/spicy/include/compiler/detail/codegen/productions/while.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::detail::codegen::production { + +/** + * A production executing as long as a given boolean expression evaluates + * to true. + */ +class While : public ProductionBase, public spicy::trait::isNonTerminal { +public: + While(const std::string& symbol, Expression e, Production body, const Location& l = location::None) + : ProductionBase(symbol, l), _expression(std::move(e)), _body(std::move(body)) {} + + const Expression& expression() const { return _expression; } + const Production& body() const { return _body; } + + // Production API + std::vector> rhss() const { return {{_body}}; } + std::optional type() const { return {}; } + bool nullable() const { return production::nullable(rhss()); } + bool eodOk() const { return nullable(); } + bool atomic() const { return false; } + bool supportsSynchronize() const { return hasSize() || _body.supportsSynchronize(); } + std::string render() const { return util::fmt("while(%s): %s", _expression, _body.symbol()); } + +private: + Expression _expression; + Production _body; +}; + +} // namespace spicy::detail::codegen::production diff --git a/spicy/include/compiler/detail/parser/driver.h b/spicy/include/compiler/detail/parser/driver.h new file mode 100644 index 000000000..e6db366d5 --- /dev/null +++ b/spicy/include/compiler/detail/parser/driver.h @@ -0,0 +1,151 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#ifdef yylex +#undef yylex +// Work-around for bison messing up the function name by adding the local namespace. +#define yylex lex +#endif + +#include + +#include +#include + +#include +#include + +#undef YY_DECL +#define YY_DECL \ + spicy::detail::parser::Parser::token_type \ + spicy::detail::parser::Scanner::lex(spicy::detail::parser::Parser::semantic_type* yylval, \ + spicy::detail::parser::location* yylloc, \ + spicy::detail::parser::Driver* driver) + +#define YYSTYPE yystype_spicy + +#ifndef __FLEX_LEXER_H +#define yyFlexLexer SpicyFlexLexer +#include + +#undef yyFlexLexer +#endif + +/** Bison value type. */ +struct yystype_spicy { + bool bool_ = false; + double real = 0.0; + uint64_t uint = 0; + int64_t sint = 0; + std::string str; + + hilti::ID id; + hilti::Declaration declaration; + hilti::Type type; + hilti::Ctor ctor; + hilti::Expression expression; + hilti::Statement statement; + hilti::Attribute attribute; + hilti::Function function; + + std::optional opt_expression; + std::optional opt_statement; + std::optional opt_attributes; + + hilti::declaration::Linkage linkage; + hilti::declaration::parameter::Kind function_parameter_kind; + hilti::function::CallingConvention function_calling_convention; + hilti::type::function::Parameter function_parameter; + hilti::type::function::Result function_result; + hilti::type::function::Flavor function_flavor; + hilti::statement::switch_::Case switch_case; + + std::vector strings; + std::vector declarations; + std::vector expressions; + std::vector statements; + std::vector function_parameters; + std::vector switch_cases; + + std::pair tuple_type_elem; + std::vector> tuple_type_elems; + + hilti::type::struct_::Field struct_field; + hilti::ctor::struct_::Field struct_elem; + std::vector struct_fields; + std::vector struct_elems; + + hilti::ctor::Map::Element map_elem; + std::vector map_elems; + + hilti::type::enum_::Label enum_label; + std::vector enum_labels; + + spicy::type::bitfield::Bits bitfield_bits_spec; + std::vector bitfield_bits; + + std::pair, std::vector> decls_and_stmts; + + // Spicy-only + std::optional opt_id; + std::vector unit_items; + spicy::type::unit::Item unit_item; + spicy::Engine engine; + std::vector hooks; + spicy::Hook hook; + + spicy::type::unit::item::switch_::Case unit_switch_case; + std::vector unit_switch_cases; +}; + +namespace spicy { + +namespace logging::debug { +inline const hilti::logging::DebugStream Parser("parser"); +} // namespace logging::debug + +namespace detail { +namespace parser { + +class Parser; +class Scanner; + +/** Driver for flex/bison. */ +class Driver { +public: + hilti::Result parse(std::istream& in, const std::string& filename); + hilti::Result parseExpression(const std::string& expression, const Meta& m = Meta()); + + Scanner* scanner() const { return _scanner; } + Parser* parser() const { return _parser; } + + // Methods for the parser. + + std::string* currentFile() { return &_filename; } + int currentLine() { return _line; } + void error(const std::string& msg, const Meta& m); + void enablePatternMode(); + void disablePatternMode(); + void enableExpressionMode(); + void disableExpressionMode(); + void enableDottedIDMode(); + void disableDottedIDMode(); + void setDestinationModule(Module m) { _module = std::move(m); } + void setDestinationExpression(Expression e) { _expression = std::move(e); } + int nextToken(); + +private: + Module _module; + Expression _expression; + std::string _filename; + int _line{}; + Parser* _parser = nullptr; + Scanner* _scanner = nullptr; + int _expression_mode = 0; + int _next_token = 0; +}; + +} // namespace parser +} // namespace detail +} // namespace spicy diff --git a/spicy/include/compiler/detail/parser/scanner.h b/spicy/include/compiler/detail/parser/scanner.h new file mode 100644 index 000000000..3df3c4a1f --- /dev/null +++ b/spicy/include/compiler/detail/parser/scanner.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +// +// This borrows from https://idlebox.net/2007/flex-bison-cpp-example. + +#pragma once + +#include + +#include + +/** We compile with a source property to find this. */ +#include <__parser.h> + +namespace spicy::detail::parser { + +/** HILTI's Flex scanner. */ +class Scanner : public SpicyFlexLexer { +public: + Scanner(std::istream* yyin = nullptr, std::ostream* yyout = nullptr) : SpicyFlexLexer(yyin, yyout) {} + + spicy::detail::parser::Parser::token_type lex(spicy::detail::parser::Parser::semantic_type* yylval, + spicy::detail::parser::location* yylloc, + spicy::detail::parser::Driver* driver); + + void enablePatternMode(); + void disablePatternMode(); + void enableExpressionMode(); + void disableExpressionMode(); + void enableDottedIDMode(); + void disableDottedIDMode(); +}; + +} // namespace spicy::detail::parser diff --git a/spicy/include/compiler/detail/visitors.h b/spicy/include/compiler/detail/visitors.h new file mode 100644 index 000000000..f85ea190d --- /dev/null +++ b/spicy/include/compiler/detail/visitors.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include + +namespace hilti { +class Unit; +} // namespace hilti +namespace hilti::printer { +class Stream; +} // namespace hilti::printer +namespace spicy { +using hilti::Result; +// namespace spicy +} // namespace spicy + +namespace spicy::detail { + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +void buildScopes(const std::vector>& modules, hilti::Unit* unit); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +bool resolveIDs(hilti::Node* root, hilti::Unit* unit); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +bool applyCoercions(hilti::Node* root, hilti::Unit* unit); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +void preTransformValidateAST(const hilti::Node& root, hilti::Unit* unit); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +void postTransformValidateAST(const hilti::Node& root, hilti::Unit* unit); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +void preservedValidateAST(const std::vector& nodes, hilti::Unit* unit); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +bool printAST(const hilti::Node& root, hilti::printer::Stream& out); // NOLINT + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +std::optional coerceCtor(hilti::Ctor c, const hilti::Type& dst, bitmask style); + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +std::optional coerceType(hilti::Type t, const hilti::Type& dst, bitmask style); + +} // namespace spicy::detail diff --git a/spicy/include/config.h.in b/spicy/include/config.h.in new file mode 100644 index 000000000..fb41152d0 --- /dev/null +++ b/spicy/include/config.h.in @@ -0,0 +1,70 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +#cmakedefine HAVE_ZEEK + +#ifdef HAVE_ZEEK +#define ZEEK_EXECUTABLE "@ZEEK_EXE@" +#define ZEEK_PREFIX "@ZEEK_PREFIX@" +#endif + +namespace spicy { + +struct Configuration { + /** + * Default constructor that initializes all settings assuming we're + * running out of the installation directory (i.e., not the build + * directory) + */ + Configuration(); + + bool uses_build_directory; /**< True if all information pertains to running outside of the build directory. */ + + std::filesystem::path spicyc; /**< Full path to `spicyc` binary */ + std::vector + spicy_library_paths; /**< Default search path for Spicy modules, separated by `:` */ + std::vector compiler_cxx_flags_debug; /**< C++ compiler flags when compiling custom code in debug mode + that uses the HILTI compiler */ + std::vector compiler_ld_flags_debug; /**< Linker flags when compiling custom code in debug mode that + uses the Spicy compiler */ + std::vector runtime_cxx_flags_debug; /**< C++ compiler flags when compiling custom code in debug mode + that uses the Spicy runtime library */ + std::vector runtime_ld_flags_debug; /**< Linker flags when compiling custom code in debug mode that + uses the Spicy runtime library */ + std::vector compiler_cxx_flags_release; /**< C++ compiler flags when compiling custom code in release + mode that uses the Spicy compiler */ + std::vector compiler_ld_flags_release; /**< Linker flags when compiling custom code in release mode + that uses the Spicy compiler */ + std::vector runtime_cxx_flags_release; /**< C++ compiler flags when compiling custom code in release + mode that uses the Spicy runtime library */ + std::vector runtime_ld_flags_release; /**< Linker flags when compiling custom code in release mode that + uses the Spicy runtime library */ + + /** + * Augments the global HILTI configuration with Spicy's options. For + * condfiguration options that are offered separately by both HILTI and + * Spicy (e.g., the compiler flags), this modifies the global HILTI + * configuration by merging in the corresponding Spicy values. This all + * reconfigures this Spicy configuration's paths based on the HILTI + * configuration's setting on whether we're running out of the build + * directory. + */ + static void extendHiltiConfiguration(); + +private: + void init(bool use_build_location); +}; + +/** + * Returns a reference to the global configuration information. This is the + * same information that `hilti-config` reports as well. + */ +extern Configuration& configuration(); + +} // namespace spicy diff --git a/spicy/include/global.h b/spicy/include/global.h new file mode 100644 index 000000000..5c8c52bb6 --- /dev/null +++ b/spicy/include/global.h @@ -0,0 +1,32 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include + +namespace spicy { + +/** + * Parses a Spicy source file into an AST. + * + * @param in stream to read from + * @param filename path associated with the input + * + * Returns: The parsed AST, or a corresponding error if parsing failed. + */ +hilti::Result parseSource(std::istream& in, const std::string& filename); + +/** + * Parses a single Spicy expression into a corresponding AST. + * + * @param expr expression to parse. + * @param m optional meta information to associate with expression + * + * Returns: The parsed expression, or a corresponding error if parsing failed. + */ +hilti::Result parseExpression(const std::string& expr, const Meta& m = Meta()); + +} // namespace spicy diff --git a/spicy/include/rt/base64.h b/spicy/include/rt/base64.h new file mode 100644 index 000000000..81d98c5b1 --- /dev/null +++ b/spicy/include/rt/base64.h @@ -0,0 +1,118 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +namespace spicy::rt::base64 { + +namespace detail { +struct State; +} // namespace detail + +/** Thrown when something goes wrong with uncompressiong. */ +class Base64Error : public hilti::rt::RuntimeError { + using hilti::rt::RuntimeError::RuntimeError; +}; + +/** + * State for streaming base64 encoding/decoding. Each instance may be be used + * only for either for encoding or decoding. + */ +class Stream { +public: + Stream(); + ~Stream(); + + Stream(const Stream&) = default; + Stream(Stream&&) noexcept = default; + Stream& operator=(const Stream&) = default; + Stream& operator=(Stream&&) noexcept = default; + + /** + * Encode a chunk of data. Each chunk will continue where the previous + * one left off. + * + * @param data next chunk of data to encode + * @return newly encoded data + */ + hilti::rt::Bytes encode(const hilti::rt::Bytes& data); + + /** + * Encode a chunk of data. Each chunk will continue where the previous + * one left off. + * + * @param data next chunk of data to encode + * @return newly encoded data + */ + hilti::rt::Bytes encode(const hilti::rt::stream::View& data); + + /** + * Decode a chunk of data. Each chunk will continue where the previous + * one left off. + * + * @param data next chunk of data to decode + * @return newly encoded data + */ + hilti::rt::Bytes decode(const hilti::rt::Bytes& data); + + /** + * Decode a chunk of data. Each chunk will continue where the previous + * one left off. + * + * @param data next chunk of data to decode + * @return newly encoded data + */ + hilti::rt::Bytes decode(const hilti::rt::stream::View& data); + + /** + * Signals the end of encoding/decoding. + * + * @return any additional data becoming available at the end of the process + */ + hilti::rt::Bytes finish(); + +private: + std::shared_ptr _state; +}; + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes encode(Stream& stream, // NOLINT(google-runtime-references) + const hilti::rt::Bytes& data) { + return stream.encode(data); +} + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes encode(Stream& stream, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& data) { + return stream.encode(data); +} + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes decode(Stream& stream, // NOLINT(google-runtime-references) + const hilti::rt::Bytes& data) { + return stream.decode(data); +} + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes decode(Stream& stream, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& data) { + return stream.decode(data); +} + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes finish(Stream& stream) // NOLINT(google-runtime-references) +{ + return stream.finish(); +} + +} // namespace spicy::rt::base64 + +namespace hilti::rt::detail::adl { +extern inline std::string to_string(const spicy::rt::base64::Stream& /* x */, adl::tag /*unused*/) { + return ""; +} +} // namespace hilti::rt::detail::adl diff --git a/spicy/include/rt/config.h.in b/spicy/include/rt/config.h.in new file mode 100644 index 000000000..b7608fc59 --- /dev/null +++ b/spicy/include/rt/config.h.in @@ -0,0 +1,8 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy::rt::configuration { +} // namespace spicy::rt::configuration diff --git a/spicy/include/rt/debug.h b/spicy/include/rt/debug.h new file mode 100644 index 000000000..d75af01bd --- /dev/null +++ b/spicy/include/rt/debug.h @@ -0,0 +1,22 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +/** Records a debug message to the `spicy` debugging stream. */ +#define SPICY_RT_DEBUG(msg) HILTI_RT_DEBUG("spicy", msg) + +/** Records a debug message to the `spicy-verbose` debugging stream. */ +#define SPICY_RT_DEBUG_VERBOSE(msg) HILTI_RT_DEBUG("spicy-verbose", msg) + +namespace spicy::rt::debug { + +using namespace hilti::rt::debug; + +/** Returns true if verbose debug logging has been requested. */ +inline bool wantVerbose() { return hilti::rt::debug::isEnabled("spicy-verbose"); } + +} // namespace spicy::rt::debug diff --git a/spicy/include/rt/driver.h b/spicy/include/rt/driver.h new file mode 100644 index 000000000..e6d51ea21 --- /dev/null +++ b/spicy/include/rt/driver.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include + +namespace spicy::rt { + +/** + * Runtime driver to retrieve and feed Spicy parsers. + * + * The HILTI/Spocy runtime environments must be managed externally, and must + * have been initialized already before using any of the driver's + * functionality. + */ +class Driver { +public: + Driver() : _enable_debug(hilti::rt::isDebugVersion()) {} + /** + * Prints a humand-readable list of all available parsers, retrieved from + * the Spicy runtime system. + * + * @param out stream to print the summary to + * @return an error if the list cannot be retrieved + */ + hilti::rt::Result listParsers(std::ostream& out); + + /** + * Retrieves a parser by its name. + * + * @param parser_name name of the parser to be retrieved, as shown in the + * output of `listParsers()`. If none is given and there's only one + * available, that one is taken. + * + * @return the parser, or an error if it could not be retrieved + */ + hilti::rt::Result lookupParser(const std::string& parser_name = ""); + + /** + * Feeds a parser with an input stream of data. + * + * @param parser parser to instantiate and feed + * @param in stream to read input data from; will read until EOF is encountered + * @param increment if non-zero, will feed the data in small chunks at a + * time; this is mainly for testing parsers; incremental parsing + * + * @return error if the input couldn't be fed to the parser (excluding parse errors) + * @throws HILTI or Spocy runtime error if the parser into trouble + */ + hilti::rt::Result processInput(const spicy::rt::Parser& parser, std::istream& in, + int increment = 0); + +private: + void _debug(const std::string_view& msg); + void _debug_stats(const hilti::rt::ValueReference& data); + + bool _enable_debug = false; +}; + +} // namespace spicy::rt diff --git a/spicy/include/rt/filter.h b/spicy/include/rt/filter.h new file mode 100644 index 000000000..c6183e47e --- /dev/null +++ b/spicy/include/rt/filter.h @@ -0,0 +1,271 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include + +namespace spicy::rt::filter { +namespace detail { + +/** Checks whether a given struct type corresponds to a Spicy filter unit. */ +template +struct is_filter : std::false_type {}; + +template +struct is_filter : std::true_type {}; + +struct OneFilter { + using Parse1Function = std::function&, + const std::optional&)>; + + Parse1Function parse; + hilti::rt::ValueReference input; + hilti::rt::Resumable resumable; +}; + +/** + * State stored inside a unit instance to capture filters it has connected to itself. + * it. + */ +using Filters = hilti::rt::Vector; + +/** + * State stored inside a unit instance when it's filtering another one's + * input. This is the data that `forward()` writes to. + */ +using Forward = hilti::rt::Stream; + +} // namespace detail + +/** + * Type holding state for filter operations inside types that can either act as + * filters or receive filtered input. + * + * \note The important thing for such types is that they offer these fields + * themselves, although not necessarily through this actual type. In particular, + * the unit structs that the Spicy code generator produces, include these fields + * directly; they are not using this type. This type is meant primarily for the + * runtime library when it needs to interface with filters (like sinks do). + * + * @tparam debug_type_name name used (only) in debug output to identify to the type + * + * \todo(robin): Can/should we switch generated unit types over to using this + * struct as well? + */ +template +struct State { + /** List of connected filters. */ + hilti::rt::StrongReference<::spicy::rt::filter::detail::Filters> __filters; + + /** Destination for data being forwarded. */ + hilti::rt::WeakReference<::spicy::rt::filter::detail::Forward> __forward; + + /** Returns true if at least one filter has been connected. */ + operator bool() const { return __filters && (*__filters).size(); } + + /** Dummy struct capturing the type's name for debug purposes. */ + using _ParserDummy = struct { const char* name; }; + + /** Pseudo-parser object. It just needs to have a `name`. */ + inline static _ParserDummy __parser = _ParserDummy{.name = debug_type_name}; +}; + +template +inline std::ostream& operator<<(std::ostream& out, State& s) { + out << s.__parser.name; + return out; +} + +/** + * Disconnects all connected filters from a unit. This is an internal method + * for cleaning up at the end; it's not exposed as a method to users as it + * would probably not being doing quite what's expected (because parsing + * would continue to use the structure being set up). + * + * @tparam S type compatible with the attribute's defined by the `State` type. + */ +template +void disconnect(S& state) { + if ( state.__filters ) { + for ( auto& f : *state.__filters ) { + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("- disconnecting existing filter unit from unit %s [%p]", S::__parser.name, &state)); + f.resumable.abort(); + } + + (*state.__filters).clear(); // Will invalidate the targets' output + } + + if constexpr ( detail::is_filter::value ) { + if ( state.__forward ) { + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("- sending EOD from filter unit %s [%p] to stream %p on disconnect", + S::__parser.name, &state, state.__forward.get())); + (*state.__forward).freeze(); + } + } +} + +template +void disconnect(UnitType& unit) { + return disconnect(*unit); +} + +/** + * Connects a filter unit to a unit for transforming parsing. This won't have + * an observable effect unit `filter::init()` is exectued (and must be called + * before that). + * + * @tparam S type compatible with the attribute's defined by the `State` + * type; this is target unit being connected to + * + * @tparam F type likewise compatible with `State; this the filter unit + * doing the transformation + */ +template +void connect(S& state, UnitRef filter_unit) { + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("- connecting filter unit %s [%p] to unit %s [%p]", F::__parser.name, + &*filter_unit, S::__parser.name, &state)); + + if ( ! state.__filters ) + state.__filters = hilti::rt::reference::make_strong<::spicy::rt::filter::detail::Filters>(); + + auto filter = detail::OneFilter{.parse = [filter_unit](hilti::rt::ValueReference& data, + const std::optional& cur) mutable + -> hilti::rt::Resumable { + auto lhs_filter_unit = filter_unit.derefAsValue(); + auto parse2 = std::any_cast>(F::__parser.parse2); + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt(" + parsing from stream %p, forwarding to stream %p", + data.get(), lhs_filter_unit->__forward.get())); + return (*parse2)(lhs_filter_unit, data, cur); + }, + .input = hilti::rt::Stream()}; + + (*state.__filters).push_back(std::move(filter)); + filter_unit->__forward = (*state.__filters).back().input; +} + +template +void connect(UnitType& unit, UnitRef filter_unit) { + return connect(*unit, filter_unit); +} + +/** + * Set up filtering for a unit if any filters have been connected. Must be + * called before parsing starts. + * + * @tparam S type compatible with the attribute's defined by the `State` type. + */ +template +hilti::rt::StrongReference init( + S& state, // NOLINT(google-runtime-references) + hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur) { + if ( ! (state.__filters && (*state.__filters).size()) ) + return {}; + + detail::OneFilter* previous = nullptr; + + for ( auto& f : *state.__filters ) { + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("- beginning to filter input for unit %s [%p]", S::__parser.name, &state)); + + if ( ! previous ) + f.resumable = f.parse(data, cur); + else + f.resumable = f.parse(previous->input, previous->input->view()); + + previous = &f; + } + + return hilti::rt::StrongReference((*state.__filters).back().input); +} + +template +hilti::rt::StrongReference init( + UnitType& unit, // NOLINT(google-runtime-references) + hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur) { + return init(*unit, data, cur); +} + +/** + * Forward data from a filter unit to the unit it's connected to. A noop if + * the unit isn't connected as a filter to anything. + * + * @tparam S type compatible with the attribute's defined by the `State` type. + */ +template +inline void forward(S& state, const hilti::rt::Bytes& data) { + if ( ! state.__forward ) { + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("- filter unit %s [%p] is forwarding \"%s\", but not connected to any unit", + S::__parser.name, &state, data)); + return; + } + + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("- filter unit %s [%p] is forwarding \"%s\" to stream %p", S::__parser.name, + &state, data, state.__forward.get())); + state.__forward->append(data); +} + +template +inline void forward(UnitType& unit, const hilti::rt::Bytes& data) { + return forward(*unit, data); +} + +/** + * Signals EOD from a filter unit to the unit it's connected to. A noop if + * the unit isn't connected as a filter to anything. + * + * @tparam S type compatible with the attribute's defined by the `State` type. + */ +template +inline void forward_eod(S& state) { + if ( ! state.__forward ) { + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("- filter unit %s [%p] is forwarding EOD, but not connected to any unit", + S::__parser.name, &state)); + return; + } + + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("- filter unit %s [%p] is forwarding EOD to stream %p", S::__parser.name, + &state, state.__forward.get())); + state.__forward->freeze(); +} + +template +inline void forward_eod(UnitType& unit) { + return forward_eod(*unit); +} + +/** + * Lets all filters in a list process as much of their pending input as + * possible. This should be called after new data has been appended to their + * input stream. + */ +inline void flush(hilti::rt::StrongReference filters) { + for ( auto& f : (*filters) ) + f.resumable.resume(); +} + +/** + * Lets all filters process as much of their pending input as possible. This + * should be called after new data has been appended to theor input stream. + * + * @tparam S type compatible with the attribute's defined by the `State` type. + */ +template +inline void flush(S& state) { + flush(state.__filters); +} + +template +inline void flush(UnitType& unit) { + flush(*unit); +} + +} // namespace spicy::rt::filter diff --git a/spicy/include/rt/global-state.h b/spicy/include/rt/global-state.h new file mode 100644 index 000000000..2f39eb49d --- /dev/null +++ b/spicy/include/rt/global-state.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include + +namespace spicy::rt { +struct Parser; +} // namespace spicy::rt + +// We collect all (or most) of the runtime's global state centrally. That's +// 1st good to see what we have (global state should be minimal) and 2nd +// helpful to ensure that JIT maps things correctly. Note that all code +// accessing any of this state is in charge of ensuring thread-safety itself. +// These globals are generally initialized through spicy::rt::init(); + +namespace spicy::rt::detail { + +/** Struct capturing all truely global runtime state. */ +struct GlobalState { + GlobalState() = default; + ~GlobalState(); + + GlobalState(const GlobalState&) = delete; + GlobalState(GlobalState&&) noexcept = delete; + GlobalState& operator=(const GlobalState&) = delete; + GlobalState& operator=(GlobalState&&) noexcept = delete; + + /** True once `hilit::init()`` has finished. */ + bool runtime_is_initialized = false; + + /** + * List of available parsers. Compiled Spicy parsers register themselves + * with this list automatically at initialization time. + */ + std::vector parsers; + + /** Map of parsers by the MIME types they handle. */ + std::map> parsers_by_mime_type; +}; + +/** + * Pointer to the global state singleton. Do not access directly, use + * `globalState()` instead. + */ +extern GlobalState* __global_state; + +/** Creates the global state singleton. */ +extern GlobalState* createGlobalState(); + +/** + * Returns the global state singleton. This creates the state the first time + * it's called. + */ +inline auto globalState() { + if ( __global_state ) + return __global_state; + + return createGlobalState(); +} + +} // namespace spicy::rt::detail diff --git a/spicy/include/rt/hilti-fwd.h b/spicy/include/rt/hilti-fwd.h new file mode 100644 index 000000000..f20b6b195 --- /dev/null +++ b/spicy/include/rt/hilti-fwd.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +namespace spicy::rt { + +/** sprintf-style string formatting. */ +template +auto fmt(const std::string& s, const Args&... args) { + return hilti::rt::fmt(s, args...); +} + +/** + * Reports an internal error and aborts execution. + * + * @note This forwards to the corresponding HILTI runtime function. + */ +inline void internalError(const std::string& msg) { hilti::rt::internalError(msg); } + +/** + * Reports an fatal error and aborts execution. + * + * @note This forwards to the corresponding HILTI runtime function. + */ +inline void fatalError(const std::string& msg) { hilti::rt::fatalError(msg); } + +} // namespace spicy::rt diff --git a/spicy/include/rt/init.h b/spicy/include/rt/init.h new file mode 100644 index 000000000..15e5a4050 --- /dev/null +++ b/spicy/include/rt/init.h @@ -0,0 +1,22 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +namespace spicy::rt { + +/** + * Initializes the Spicy runtime library. This must be called once at + * startup before any other libspicy functionality can be used. + */ +extern void init(); + +/** + * Shuts down the runtime library, freeing all resources. Once executed, no + * libspicy functioality can be used anymore. + */ +extern void done(); + +/** Returns true if init() has already been called. */ +extern bool isInitialized(); + +} // namespace spicy::rt diff --git a/spicy/include/rt/libspicy.h b/spicy/include/rt/libspicy.h new file mode 100644 index 000000000..9acd59c75 --- /dev/null +++ b/spicy/include/rt/libspicy.h @@ -0,0 +1,19 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include diff --git a/spicy/include/rt/mime.h b/spicy/include/rt/mime.h new file mode 100644 index 000000000..d446c6346 --- /dev/null +++ b/spicy/include/rt/mime.h @@ -0,0 +1,112 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +namespace spicy::rt { + +namespace mime { + +/** + * Exception thrown by MIMEType if it cannot parse a type specification. + */ +class InvalidType : public hilti::rt::UserException { +public: + using hilti::rt::UserException::UserException; +}; +} // namespace mime + +/** + * Type representing a MIME type consisting of main type and a subtype. + */ +class MIMEType { +public: + /** + * Initializes a MIME type from provided main and sub type. + * + * @param main main type, with '*' meaning a catch-all wildcard. + * + * @param sub main type, with '*' meaning a catch-all wildcard. + */ + MIMEType(std::string_view main, std::string_view sub) : _main(main), _sub(sub) {} + + /** + * Initializes a MIME type from provided string of the form `main/sub`. + * + * @param mt string `main/sub` + * + * @throws `mime::InvalidType` if it cannot parse the type + */ + MIMEType(const std::string& type) { + if ( type == "*" ) { + _main = _sub = "*"; + return; + } + + auto x = hilti::rt::split1(type, "/"); + _main = hilti::rt::trim(x.first); + _sub = hilti::rt::trim(x.second); + + if ( _main.empty() || _sub.empty() ) + throw mime::InvalidType(hilti::rt::fmt("cannot parse MIME type %s", type)); + } + + MIMEType() = delete; + + /** Returns the main type, with '*' reflecting a wildcard. */ + std::string mainType() const { return _main; }; + + /** Returns the sub type, with '*' reflecting a wildcard. */ + std::string subType() const { return _sub; }; + + ~MIMEType() = default; + MIMEType(const MIMEType&) = default; + MIMEType(MIMEType&&) noexcept = default; + MIMEType& operator=(const MIMEType&) = default; + MIMEType& operator=(MIMEType&&) noexcept = default; + + operator std::string() const { return mainType() + "/" + subType(); } + + /** + * Converts the type into textual key suitable for using as an index in map. + * + * @return If the main type is a wilcard, returns an empty string. If the + * sub type is a wildcard, returns just the main type. Otherwise returns + * the standard `main/sub` form. + */ + std::string asKey() const { + if ( _main == "*" ) + return ""; + + if ( _sub == "*" ) + return _main; + + return *this; + } + + /** + * Parses a string `a/b` into a MIME tyoe. + * + * @param string of the form `main/sub`. + * @return parsed type, or an error if not parseable + */ + static hilti::rt::Result parse(const std::string& s) { + try { + return MIMEType(s); + } catch ( const mime::InvalidType& e ) { + return hilti::rt::result::Error(e.description()); + } + } + +private: + std::string _main; /**< Main type. */ + std::string _sub; /**< sub type. */ +}; + +} // namespace spicy::rt + +namespace hilti::rt::detail::adl { +extern inline std::string to_string(const spicy::rt::MIMEType& x, adl::tag /*unused*/) { return x; } +} // namespace hilti::rt::detail::adl diff --git a/spicy/include/rt/parser.h b/spicy/include/rt/parser.h new file mode 100644 index 000000000..5eec6177d --- /dev/null +++ b/spicy/include/rt/parser.h @@ -0,0 +1,253 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace spicy::rt { + +/** + * Runtime information about an available parser. + * + * Note: When changing this struct, adapt the Spicy-side `spicy_rt::Parser` + * as well. + */ +struct Parser { + Parser(std::string name, Parse1Function parse1, std::any parse2, std::string description, + hilti::rt::Vector mime_types, hilti::rt::Vector ports) + : name(std::move(name)), + parse1(parse1), + parse2(std::move(parse2)), + description(std::move(description)), + mime_types(std::move(mime_types)), + ports(std::move(ports)) {} + + Parser(std::string name, hilti::rt::Null /* null */, std::any parse2, std::string description, + hilti::rt::Vector mime_types, hilti::rt::Vector ports) + : Parser(std::move(name), nullptr, parse2, std::move(description), std::move(mime_types), std::move(ports)) {} + + Parser(const Parser&) = default; + + Parser() = default; + ~Parser() = default; + Parser(Parser&&) noexcept = default; + Parser& operator=(const Parser&) = default; + Parser& operator=(Parser&&) noexcept = default; + + /** Short descriptive name. */ + std::string name; + + /** Function performing parsing of given input into a temporary instance. */ + Parse1Function parse1{}; + + /** + * Function performing parsing of given input into a provided instance. + * The actual type of this member is Parse2Function. + */ + std::any parse2; + + /** + * Human-readable description associated with this parser. + */ + std::string description; + + /** + * MIME types this parer can handle. + */ + hilti::rt::Vector mime_types; + + /** + * Well-known ports associated with this parser. + */ + hilti::rt::Vector ports; + + /** + * For internal use only. Set by `registerParser()` for units that's don't + * receive arguments. + */ + std::optional __parse_sink; + + /** For internal use only. Dispatcher for the corresponding unit hook. */ + std::optional> __hook_gap; + + /** For internal use only. Dispatcher for the corresponding unit hook. */ + std::optional> + __hook_overlap; + + /** For internal use only. Dispatcher for the corresponding unit hook. */ + std::optional> __hook_skipped; + + /** For internal use only. Dispatcher for the corresponding unit hook. */ + std::optional> + __hook_undelivered; +}; + +/** Returns all available parsers. */ +inline const auto& parsers() { return detail::globalState()->parsers; } + +namespace detail { + +/** + * Registers a parser with the runtime as being available. This is + * automatically called for generated parsers during their initialization. + * + * @tparam The `UnitRef` type for the unit that the parser parses, as + * passed when calling into the runtime. + * + * @param parser parser to register. + * + * @param instance arbitrary instance of `Unit`; this is not actually used, + * we add the parameters just so that the template parameter `Unit` can be + * automatically inferred. + */ +template +inline void registerParser(::spicy::rt::Parser& p, // NOLINT(google-runtime-references) + UnitRef /* not used, just for template instantiation */) { + // Note: This may may be called before spicy::rt::init(), and during + // hilti::rt::init(). Cannot rely on any library functionality being + // intialized yet. + globalState()->parsers.emplace_back(&p); + + using unit_type = typename UnitRef::element_type; + + if constexpr ( sink::detail::supports_sinks::value ) { + if constexpr ( ! std::is_base_of::value ) + p.__parse_sink = sink::detail::parseFunction(); + + p.__hook_gap = sink::detail::hookFunction(); + p.__hook_skipped = sink::detail::hookFunction(); + p.__hook_overlap = sink::detail::hookFunction(); + p.__hook_undelivered = sink::detail::hookFunction(); + } + + for ( const auto& mt : p.mime_types ) { + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("registering parser %s for MIME type %s", p.name, std::string(mt))); + auto& parsers = globalState()->parsers_by_mime_type[mt.asKey()]; + parsers.push_back(&p); + } +} + +/** + * Prints the current parser state, as passed in through arguments, to the + * spicy-verbose debug stream. + */ +void printParserState(const std::string& unit_id, const hilti::rt::ValueReference& data, + const hilti::rt::stream::View& cur, int64_t lahead, + const hilti::rt::stream::SafeConstIterator& lahead_end, const std::string& literal_mode, + bool trim); + +/** + * Used by generated parsers to wait until a minimum amount of input becomes + * available or end-of-data is reached. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @param min desired number of bytes + * @param filter filter state associated with current unit instance (which may be null) + * @return true if mininum number of bytes are available; false if end-of-data + * has been reached + */ +extern bool waitForInputOrEod(hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur, uint64_t min, + hilti::rt::StrongReference filters); + +/** + * Used by generated parsers to wait until end-of-data is obtained, but not + * necessarily reached. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @param filter filter state associated with current unit instance (which may be null) + */ +extern void waitForEod(hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur, + hilti::rt::StrongReference filters); + +/** + * Used by generated parsers to wait until a minimum amount of input becomes + * available. If a end-of-data is reached before that, will trigger a parse + * error. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @param min desired number of bytes + * @param error_msg message to report with parse error if end-of-data is been reached + * @param location location associated with the situation + * @param filter filter state associated with current unit instance (which may be null) + * @return true if mininum number of bytes are available; false if end-of-data + * has been reached + */ +extern void waitForInput(hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur, uint64_t min, const std::string& error_msg, + const std::string& location, + hilti::rt::StrongReference filters); + +/** + * Used by generated parsers to wait more input becomes available or + * end-of-data is reached. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @param filter filter state associated with current unit instance (which may be null) + * @return true if mininum number of bytes are available; false if end-of-data + * has been reached + */ +extern bool waitForInputOrEod(hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur, + const hilti::rt::StrongReference& filters); + +/** + * Used by generated parsers to wait until more input becomes available. If a + * end-of-data is reached before any more data becomes available, will + * trigger a parse error. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @param error_msg message to report with parse error if end-of-data is been reached + * @param location location associated with the situation + * @param filter filter state associated with current unit instance (which may be null) + * @return true if mininum number of bytes are available; false if end-of-data + * has been reached + */ +extern void waitForInput(hilti::rt::ValueReference& data, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& cur, const std::string& error_msg, const std::string& location, + const hilti::rt::StrongReference& filters); + +/** + * Used by generated parsers to recognize end-of-data. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @return true if end-of-data has been reached + */ +extern bool atEod(const hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur); + +/** + * Used by generated parsers to recognize when end-of-data has been seen, + * but possibly not reached. + * + * @param data current input data + * @param cur view of *data* that's being parsed + * @return true if end-of-data has been seen, but not necessarily reached. + */ +extern bool haveEod(const hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur); +} // namespace detail +} // namespace spicy::rt diff --git a/spicy/include/rt/sink.h b/spicy/include/rt/sink.h new file mode 100644 index 000000000..1b22bcb83 --- /dev/null +++ b/spicy/include/rt/sink.h @@ -0,0 +1,351 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace spicy::rt { + +namespace sink { +enum class ReassemblerPolicy { First }; + +/** + * Exception thrown when sink operations fail due to usage errors. + */ +class Error : public hilti::rt::UserException { +public: + using hilti::rt::UserException::UserException; +}; + +} // namespace sink +namespace sink::detail { + +/** Checks whether a given struct type corresponds to a unit that can be connected to a sink. */ +template +struct supports_sinks : std::false_type {}; + +template +struct supports_sinks : std::true_type {}; + +/** State for a sink stored the unit it's connected to. */ +struct State { + /** Data being parsed. */ + hilti::rt::ValueReference data; + + /** Resumable parse function. */ + hilti::rt::Resumable resumable; + + Parser* parser; +}; + +/** + * Helper function that kicks off parsing for a unit going to be connected to + * a sink. + */ +template +auto _connectUnit(UnitRef& unit) { + auto parse2 = std::any_cast>(U::__parser.parse2); + + auto self = hilti::rt::ValueReference::self(&*unit); + + auto& state = unit->__sink; + state = new sink::detail::State(); // NOLINT + state->resumable = (*parse2)(self, state->data, {}); // Kick-off parsing with empty data. + state->parser = &U::__parser; + return state; +} + +/** + * Helper function that produces a function that, when called, kicks of + * parsing for a freshly instantiated unit going to be connected to a sink. + * This function will be stored in the `parser` object for the unit. + */ +template +::spicy::rt::detail::ParseSinkFunction parseFunction() { + return []() { + auto unit = UnitRef(U()); + return std::make_pair(std::move(unit), _connectUnit(unit)); + }; +} + +/** + * Returns a function for storing in a unit's `Parser` instance that enables + * the runtime library to run a specific hook, given a generic unit pointer. + * + * @tparam Unit unit type that the hook belongs to + * @tparam Member function pointer to hook + * @tparam Args hook parameters + * @return function of type `void (hilti::rt::StrongReferenceGeneric, ` + */ +template +auto hookFunction() { + return [](const hilti::rt::StrongReferenceGeneric& u, Args&&... args) -> void { + auto unit = u.as(); + ((*unit).*Hook)(std::forward(args)...); + }; +} + +// Name used as template parameter for sink's filter state. */ +inline const char sink_name[] = "__sink__"; +} // namespace sink::detail + +/** + * Runtime implementation for Spicy's `sink` type. + * + * Note: When adding/changing methods that generated code acceeses, adapt the + * Spicy-side `spicy_rt::Sink` as well. + */ + +class Sink { +public: + Sink() { _init(); } // NOLINT(hicpp-member-init) + ~Sink() { _close(true); } + + Sink(const Sink&) = delete; + Sink(Sink&&) noexcept = default; + Sink& operator=(const Sink&) = delete; + Sink& operator=(Sink&&) noexcept = default; + + /** + * Connects a unit instance to the sink. The unit will then receive any + * data written into the sink. + * + * @param unit unit to connect to the sink. + */ + template + void connect(spicy::rt::UnitRef unit) { + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("connecting parser %s [%p] to sink %p", T::__parser.name, &*unit, this)); + auto state = spicy::rt::sink::detail::_connectUnit(unit); + _units.emplace_back(std::move(unit)); + _states.emplace_back(std::move(state)); + } + + /** + * Connects a filter unit to the sink. Any input will then pass through the + * filter before being forwarded tp parsing. Must not be called when data + * has been processed already. Multiple filters can be connected and will be + * chained. + * + * @param filter filter unit to connect to the sink. + * @throws ``sink::Error`` if the type cannot be parsed + */ + template + void connect_filter(spicy::rt::UnitRef unit) { + if ( _size ) + throw sink::Error("cannot connect filter after data has been forwarded already"); + + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("connecting filter unit %s [%p] to sink %p", T::__parser.name, &*unit, this)); + spicy::rt::filter::connect(_filter, unit); + } + + /** + * Disconnects all units connected to the sink. They will then no longer + * receive any data written into the sink. + */ + void close() { _close(true); } + + /** + * Connects new instances of all units to the sink that support a given + * MIME type. The units will then all receive any data written into the + * think. + * + * @param mt MIME type to connect units for + */ + void connect_mime_type(const MIMEType& mt); + + /** + * Connects new instances of all units to the sink that support a given + * MIME type. The units will then all receive any data written into the + * think. + * + * @param mt MIME type to connect units for + * @throws ``mime::InvalidType`` if the type cannot be parsed + */ + void connect_mime_type(const std::string& mt) { connect_mime_type(MIMEType(mt)); } + + /** + * Connets instances of all units to the sink that support a given MIME + * type. The units will then all receive any data written into the think. + * + * @param mt MIME type to connect units for + * @throws ``mime::InvalidType`` if the type cannot be parsed + */ + void connect_mime_type(const hilti::rt::Bytes& mt) { connect_mime_type(MIMEType(mt.str())); } + + /** + * Reports a gap in the input stream. + * + * @param seq absolute sequence number of the gap + * @param len length of the gap + */ + void gap(uint64_t seq, uint64_t len); + + /** + * Returns the current position in the sequence space. + */ + uint64_t sequence_number() const { return _initial_seq + _cur_rseq; } + + /** + * Enable/disable automatic trimming. + * + * @param enable true to enable trimming, false to disable + */ + void set_auto_trim(bool enable) { _auto_trim = enable; } + + /** + * Sets the initial sequence number. + * + * @param seq absolute sequence number to associate with 1st byte of input. + */ + void set_initial_sequence_number(uint64_t seq) { + if ( _haveInput() ) { + _close(false); + throw sink::Error("sink cannot update initial sequence number after activity has already been seen"); + } + + _initial_seq = seq; + } + + /** Sets the sink's reassembler policy. */ + void set_policy(sink::ReassemblerPolicy policy) { _policy = policy; } + + /** + * Returns the number of bytes written into the sink so far. + */ + uint64_t size() const { return _size; } + + /** + * Skips ahead in the input stream. + * + * @param seq absolute sequence number to skip ahead to + */ + void skip(uint64_t seq); + + /** + * Trims buffered input. + * + * @param seq absolute sequence number to trim up to. + */ + void trim(uint64_t seq); + + /** + * Writes data to the sink, forwarding it to all connected units. + * + * @param data data to write + * @param seq absolute sequence number; defaults to end of current input + * @param len length in sequence space; defaults to length of *data* + */ + void write(hilti::rt::Bytes data, std::optional seq = {}, std::optional len = {}); + + /** + * Tracks connected filters. This is internal, but needs to be public + * because some free-standing functions are accessing it. + * + * \todo(robin): We could probably declared the corresponding instantiations + * as friends. + */ + filter::State _filter; + +private: + struct Chunk { + std::optional data; // Data at +1; unset for gap + uint64_t rseq; // Sequence number of first byte. + uint64_t rupper; // Sequence number of last byte + 1. + + Chunk(std::optional data, uint64_t rseq, uint64_t rupper) + : data(std::move(data)), rseq(rseq), rupper(rupper) {} + }; + + using ChunkList = std::list; + + // Returns true if any input has been passed in already (including gaps). + bool _haveInput() { return _cur_rseq || _chunks.size(); } + + // Backend for disconnecting the sink. If orderly, connected units get a + // chance to parse any remaining input; otherwise we abort directly. + void _close(bool orderly); + + // Turns an absolute sequence number into a relative one. + uint64_t _rseq(uint64_t seq) const { + // I believe this does the right thing for wrap-around ... + return seq - _initial_seq; + } + + // Turns a relative sequence number into an absolute one. + uint64_t _aseq(uint64_t rseq) const { + // I believe this does the right thing for wrap-around ... + return _initial_seq + rseq; + } + + // (Re-)initialize instance. + void _init(); + + // Add new data to buffer, beginning search for insert position at given start *c*. + ChunkList::iterator _addAndCheck(std::optional data, uint64_t rseq, uint64_t rupper, + ChunkList::iterator c); + + // Deliver data to connected parsers. Returns false if the data is empty (i.e., a gap). + bool _deliver(std::optional data, uint64_t rseq, uint64_t rupper); + + // Entry point for all new data. If not bytes instance is given, that signals a gap. + void _newData(std::optional data, uint64_t rseq, uint64_t len); + + // Skip up to sequence number. + void _skip(uint64_t rseq); + + // Trim up to sequence number. + void _trim(uint64_t rseq); + + // Deliver as much as possible starting at given buffer position. + void _tryDeliver(ChunkList::iterator c); + + // Trigger various hooks. + void _reportGap(uint64_t rseq, uint64_t len) const; + void _reportOverlap(uint64_t rseq, const hilti::rt::Bytes& old, const hilti::rt::Bytes& new_) const; + void _reportSkipped(uint64_t rseq) const; + void _reportUndelivered(uint64_t rseq, const hilti::rt::Bytes& data) const; + void _reportUndeliveredUpTo(uint64_t rupper) const; + + // Output reassembler state for debugging. + void _debugReassembler(const std::string& msg, const std::optional& data, uint64_t seq, + uint64_t len) const; + void _debugReassemblerBuffer(const std::string& msg) const; + void _debugDeliver(const hilti::rt::Bytes& data) const; + + // States for connected units. + std::vector _states; + + // Must come after `_state` as it's keeping the states around. + std::vector _units; + + // Filter input and output. + struct FilterData { + hilti::rt::ValueReference input; + hilti::rt::StrongReference output; + hilti::rt::stream::View output_cur; + }; + + std::optional _filter_data; + + // Reassembly state. + sink::ReassemblerPolicy _policy; // Current policy + bool _auto_trim{}; // True if automatic trimming is enabled. + uint64_t _size{}; + uint64_t _initial_seq{}; // Initial sequence number. + uint64_t _cur_rseq{}; // Sequence of last delivered byte + 1 (i.e., seq of next) + uint64_t _last_reassem_rseq{}; // Sequence of last byte reassembled and delivered + 1. + uint64_t _trim_rseq{}; // Sequence of last byte trimmed so far + 1. + ChunkList _chunks; // Buffered data not yet delivered or trimmed +}; + +} // namespace spicy::rt + +namespace hilti::rt::detail::adl { +extern inline std::string to_string(const spicy::rt::Sink& /* x */, adl::tag /*unused*/) { return ""; } +} // namespace hilti::rt::detail::adl diff --git a/spicy/include/rt/spicy.h b/spicy/include/rt/spicy.h new file mode 100644 index 000000000..a7b4c7b4a --- /dev/null +++ b/spicy/include/rt/spicy.h @@ -0,0 +1,8 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * This header defines functions that are made available to Spicy grammars + * inside the `spicy::*` namespace. + */ + +#pragma once diff --git a/spicy/include/rt/typedefs.h b/spicy/include/rt/typedefs.h new file mode 100644 index 000000000..bfb8166a0 --- /dev/null +++ b/spicy/include/rt/typedefs.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy::rt { + +template +using UnitType = hilti::rt::ValueReference; + +template +using UnitRef = hilti::rt::StrongReference; + +/** Defines the type of the generic version of units' public parsing functions. */ +using Parse1Function = hilti::rt::Resumable (*)(hilti::rt::ValueReference&, + const std::optional&); + +/** Defines the type of the generic version of units' public parsing functions. */ +template +using Parse2Function = hilti::rt::Resumable (*)(UnitType&, hilti::rt::ValueReference&, + const std::optional&); + +/** + * Exception thrown by generated parser code when an parsing failed. + */ +class ParseError : public hilti::rt::UserException { +public: + ParseError(const std::string& msg) : UserException(hilti::rt::fmt("parse error: %s", msg)) {} + + ParseError(const std::string& msg, const std::string& location) + : UserException(hilti::rt::fmt("parse error: %s", msg), location) {} + + ParseError(const hilti::rt::result::Error& e) : UserException(hilti::rt::fmt("parse error: %s", e.description())) {} +}; + +namespace sink::detail { +struct State; +} // namespace sink::detail + +namespace detail { + +/** + * + * Defines the type of a unit's parse function used when connected to a sink. + * This is for internal use only. + */ +using ParseSinkFunction = + std::function()>; +} // namespace detail + +} // namespace spicy::rt diff --git a/spicy/include/rt/util.h b/spicy/include/rt/util.h new file mode 100644 index 000000000..f374a71f6 --- /dev/null +++ b/spicy/include/rt/util.h @@ -0,0 +1,12 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +namespace spicy::rt { + +/** Returns a string identifying the version of the runtime library. */ +extern std::string version(); + +} // namespace spicy::rt diff --git a/spicy/include/rt/zlib.h b/spicy/include/rt/zlib.h new file mode 100644 index 000000000..5e65d8fe0 --- /dev/null +++ b/spicy/include/rt/zlib.h @@ -0,0 +1,86 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace spicy::rt::zlib { + +namespace detail { +struct State; +} // namespace detail + +/** Thrown when something goes wrong with uncompressiong. */ +class ZlibError : public hilti::rt::RuntimeError { + using hilti::rt::RuntimeError::RuntimeError; +}; + +/** + * State for streaming gzip decompression. + */ +class Stream { +public: + Stream(); + ~Stream(); + + Stream(const Stream&) = default; + Stream(Stream&&) noexcept = default; + Stream& operator=(const Stream&) = default; + Stream& operator=(Stream&&) noexcept = default; + + /** + * Decompresses a chunk of data. Each chunk will continue where the + * previous one left off. + * + * @param data next chunk of data to decompress + * @return newly decompressed data + */ + hilti::rt::Bytes decompress(const hilti::rt::Bytes& data); + + /** + * Decompresses a chunk of data. Each chunk will continue where the + * previous one left off. + * + * @param data next chunk of data to decompress + * @return newly decompressed data + */ + hilti::rt::Bytes decompress(const hilti::rt::stream::View& data); + + /** + * Signals the end of decompression. + * + * @return any additional data becoming available at the end of the process + */ + hilti::rt::Bytes finish(); + +private: + std::shared_ptr _state; +}; + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes decompress(Stream& stream, // NOLINT(google-runtime-references) + const hilti::rt::Bytes& data) { + return stream.decompress(data); +} + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes decompress(Stream& stream, // NOLINT(google-runtime-references) + const hilti::rt::stream::View& data) { + return stream.decompress(data); +} + +/** Forwards to the corresponding `Stream` method. */ +inline hilti::rt::Bytes finish(Stream& stream) // NOLINT(google-runtime-references) +{ + return stream.finish(); +} + +} // namespace spicy::rt::zlib + +namespace hilti::rt::detail::adl { +extern inline std::string to_string(const spicy::rt::zlib::Stream& /* x */, adl::tag /*unused*/) { + return ""; +} +} // namespace hilti::rt::detail::adl diff --git a/spicy/include/spicy b/spicy/include/spicy new file mode 120000 index 000000000..f5030fe88 --- /dev/null +++ b/spicy/include/spicy @@ -0,0 +1 @@ +../include \ No newline at end of file diff --git a/spicy/include/spicy.h b/spicy/include/spicy.h new file mode 100644 index 000000000..bd3f30f1b --- /dev/null +++ b/spicy/include/spicy.h @@ -0,0 +1,9 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include +#include diff --git a/spicy/lib/filter.spicy b/spicy/lib/filter.spicy new file mode 100644 index 000000000..76712a40e --- /dev/null +++ b/spicy/lib/filter.spicy @@ -0,0 +1,35 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +module filter; + +import spicy; + +## A filter that performs zlib decompression. +type Zlib = unit { + %filter + + data: bytes &chunked &eod { + self.forward(spicy::zlib_decompress(self.z, self.data)); + } + + on %done { + self.forward(spicy::zlib_finish(self.z)); + } + + var z: spicy::ZlibStream; +}; + +## A filter that performs Base64 decoding. +type Base64Decode = unit { + %filter + + data: bytes &chunked &eod { + self.forward(spicy::base64_decode(self.z, self.data)); + } + + on %done { + self.forward(spicy::base64_finish(self.z)); + } + + var z: spicy::Base64Stream; +}; diff --git a/spicy/lib/protocols/dns.spicy b/spicy/lib/protocols/dns.spicy new file mode 100644 index 000000000..2afce698b --- /dev/null +++ b/spicy/lib/protocols/dns.spicy @@ -0,0 +1,146 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +module DNS; + +import spicy; + +type RDType = enum { + A = 1, NS = 2, MD = 3, MF = 4, CNAME = 5, SOA = 6, MB = 7, MG = 8, MR = 9, + NULL = 10, WKS = 11, PTR = 12, HINFO = 13, MINFO = 14, MX = 15, TXT = 16, + AAAA = 28, NBS = 32, SRV= 33, A6 = 38, EDNS = 41 }; + +type RRType = enum { + ANSWER = 1, AUTH = 2, ADDL = 3 +}; + +public type Message = unit { + %random-access + + header: Header; + question: (Question(self))[self.header.qdcount]; + answer: (ResourceRecord(self, RRType::ANSWER))[self.header.ancount]; + authority: (ResourceRecord(self, RRType::AUTH))[self.header.nscount]; + additional: (ResourceRecord(self, RRType::ADDL))[self.header.arcount]; +}; + +type Header = unit { + id : uint16; + flags : bitfield(16) { + qr: 0; + opcode: 1..4; + aa: 5; + tc: 6; + rd: 7; + ra: 8; + z: 9..11; + rcode: 12..15; + } &bit-order = spicy::BitOrder::MSB0; + + qdcount: uint16; + ancount: uint16; + nscount: uint16; + arcount: uint16; + + var rejected : bool; + + on %done { + # Mimic Zeek in determining when a request has been rejected. + if ( self.qdcount == 0 ) + self.rejected = (self.flags.rcode != 0); # 0 == NoError; + + else + self.rejected = (self.flags.qr == 1 && + self.ancount == 0 && + self.nscount == 0 && + self.arcount == 0); + } +}; + +type Question = unit(msg: Message) { + qname: Name(msg); + qtype: uint16; + qclass: uint16; +}; + +type ResourceRecord = unit(msg: Message, rrtype: RRType) { + name: Name(msg); + ty: uint16 &convert=RDType($$); + class: uint16; + ttl: uint32 &convert=cast($$); + rdlen: uint16; + + switch ( self.ty ) { + RDType::NS, RDType::CNAME, RDType::PTR + -> rname: Name(msg); + RDType::A -> a: addr &ipv4; + RDType::AAAA -> a: addr &ipv6; + RDType::MX -> mx: RDataMX(msg); + RDType::SOA -> soa: RDataSOA(msg); + RDType::TXT -> txt: (CharacterString(msg))[self.rdlen]; + + * -> rdata: bytes &size=self.rdlen; + }; +}; + +type RDataMX = unit(msg: Message) { + preference: uint16; + name: Name(msg); +}; + +type RDataSOA = unit(msg: Message) { + mname: Name(msg); + rname: Name(msg); + serial: uint32; + refresh: uint32 &convert=cast($$); + retry: uint32 &convert=cast($$); + expire: uint32 &convert=cast($$); + minimum: uint32 &convert=cast($$); +}; + +type CharacterString = unit(msg: Message) { + len: uint8; + data: bytes &size=(self.len); +}; + +type Name = unit(msg: Message) { + : (Label(msg, self))[] &until=($$.len.offset == 0 || $$.len.compressed != 0); + + # Parsed labels are appended here. + var label: bytes = b""; +}; + +type Pointer = unit(msg: Message, label: Label) { + len: bitfield(16) { + offset: 0..13; + }; + + name: Name(msg) &parse-at=(msg.input() + self.len.offset); + }; + +type Label = unit(msg: Message, inout name: Name) { + %random-access + + len: bitfield(8) { + offset: 0..5; + compressed: 6..7; + }; + + switch ( self.len.compressed ) { + 0 -> label: bytes &size=self.len.offset { + name.label += b"."; + name.label += self.label; + } + + 3 -> ptr: Pointer(msg, self) &parse-at=self.input() { + name.label += self.ptr.name.label; + self.adjust = 2; # Consume the additional byte in %done. + } + }; + + on %done { + if ( self.adjust > 0 ) + self.set_input(self.input() + self.adjust); + } + + var adjust: uint64 = 0; +}; diff --git a/spicy/lib/protocols/http.spicy b/spicy/lib/protocols/http.spicy new file mode 100644 index 000000000..f7c911135 --- /dev/null +++ b/spicy/lib/protocols/http.spicy @@ -0,0 +1,225 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. +# +# TODO: +# +# - Continued headers +# - decode URIs +# - connection: keep-alive, close +# - software versions +# - partial content + +module HTTP; + +import spicy; +import filter; + +type DeliveryMode = enum { EndOfData, Length, Multipart, Chunked }; + +const Token = /[^ \t\r\n]+/; +const URI = /[^ \t\r\n]+/; +const NewLine = /\r?\n/; +const RestOfLine = /[^\r\n]*/; +const FullLine = /[^\r\n]*\r?\n/; +const Integer = /[0-9]+/; +const HexInteger = /[0-9a-zA-Z]+/; +const WhiteSpace = /[ \t]+/; +const OptionalWhiteSpace = /[ \t]*/; + +function reply_code_needs_body(status: uint64) : bool { + return status <= 99 || (status >= 200 && status != 204 && status != 304); +} + +public type Requests = unit { + : Request[]; +}; + +type Request = unit { + request: RequestLine; + message: Message(False, True); +}; + +public type Replies = unit { + : Reply[]; +}; + +type Reply = unit { + reply: ReplyLine; + message: Message(reply_code_needs_body(self.reply.status), False); +}; + +type Version = unit { + : /HTTP\//; + number: /[0-9]+\.[0-9]*/; +}; + +type RequestLine = unit { + method: Token; + : WhiteSpace; + uri: URI; + : WhiteSpace; + version: Version; + : NewLine; +}; + +type ReplyLine = unit { + version: Version; + : WhiteSpace; + status: Integer &convert=$$.to_uint(); + : OptionalWhiteSpace; + reason: RestOfLine; + : NewLine; +}; + +type Message = unit(body_default: bool, is_request: bool) { + headers: (Header(self))[]; + end_of_hdr: NewLine; + body: Body(self, self.delivery_mode, is_request) if ( self.has_body ); + + var has_body: bool; + var is_request: bool; + + var content_encoding: bytes &optional; + var use_content_length: bool = True; + var body_len: uint64 &optional; + var content_length: uint64 &optional; + var content_type: tuple = (b"TEXT", b"PLAIN"); + var content_type_parameter: bytes &optional; + var delivery_mode: DeliveryMode = DeliveryMode::EndOfData; + var multipart_boundary: bytes &optional; + var transfer_encoding: bytes &optional; + + on %init { + self.has_body = body_default; + self.is_request = is_request; + } + + on end_of_hdr { + if ( self?.content_length ) + self.delivery_mode = DeliveryMode::Length; + + if ( self.content_type[0] == b"MULTIPART" ) { + local boundary = self.content_type_parameter.match(/boundary="([^"]*)"/, 1); + + if ( ! boundary ) + boundary = self.content_type_parameter.match(/boundary=([^ ;]*)/, 1); + + self.delivery_mode = DeliveryMode::Multipart; + self.multipart_boundary = b"--" + *boundary + b"--\r\n"; + } + + if ( self?.transfer_encoding && self.transfer_encoding == b"chunked" ) + self.delivery_mode = DeliveryMode::Chunked; + } +}; + +const HeaderName = /[^:\r\n]+/; +const HeaderValue = /[^\r\n]*/; + +type Header = unit(inout msg: Message) { + name: HeaderName &convert=$$.upper(); + : /:[\t ]*/; + content: HeaderValue; + : NewLine; + + on content { + if ( self.name == b"CONTENT-LENGTH" ) { + msg.content_length = self.content.to_uint(); + msg.has_body = True; + } + + if ( self.name == b"TRANSFER-ENCODING" ) { + msg.transfer_encoding = self.content; + msg.has_body = True; + } + + if ( self.name == b"CONTENT-ENCODING" ) + msg.content_encoding = self.content; + + if ( self.name == b"CONTENT-TYPE" ) { + local ct: tuple = self.content.split1(b";"); + local ty: tuple = ct[0].split1(b"/"); + msg.content_type = (ty[0].strip().upper(), ty[1].strip().upper()); + msg.content_type_parameter = ct[1].strip(); + } + } +}; + +type Body = unit(inout msg: Message, delivery_mode: DeliveryMode, is_request: bool) { + switch ( delivery_mode ) { + + DeliveryMode::EndOfData -> : bytes &eod &chunked -> self.data; + + DeliveryMode::Length -> : bytes &size=msg.content_length &eod &chunked -> self.data; + + DeliveryMode::Multipart -> : FullLine[] &until=($$ == msg.multipart_boundary) + foreach { self.data.write($$); } + + DeliveryMode::Chunked -> : Chunks(self, msg); + }; + + sink data; + + on %init priority=20 { + if ( msg.content_type[0] != b"" && msg.content_type[1] != b"" ) + self.data.connect_mime_type(msg.content_type[0] + b"/" + msg.content_type[1]); + + if ( msg?.content_encoding ) { + if ( msg.content_encoding == b"gzip" ) { + self.data.connect_filter(new filter::Zlib); + msg.use_content_length = False; + } + + if ( msg.content_encoding == b"deflate" ) { + self.data.connect_filter(new filter::Zlib); + msg.use_content_length = False; + } + } + + self.data.connect(new Content(msg)); + } + + on %done priority=15 { + # TODO: Does this FIXME still apply? + # + # FIXME: Currently the sink becomes invalid after parsing for + # the current instance finished. That's not great. See + # ParserBuilder::_finalizeParseObject() to fix. + msg.body_len = |self.data|; + } + + on %error priority=15 { + # TODO: Does this FIXME still apply? + # + # FIXME: Currently the sink becomes invalid after parsing for + # the current instance finished. That's not great. See + # ParserBuilder::_finalizeParseObject() to fix. + msg.body_len = |self.data|; + } +}; + +public type Content = unit(msg: Message) { + data: bytes &chunked &eod { + if ( |self.data| > 0 ) { + self.have_content = True; + # Bro::file_data_in(self.data); + } + } + + var have_content: bool = False; +}; + +type Chunks = unit(inout body: Body, inout msg: Message) { + : (Chunk(body))[] &until=($$.length == 0); + trailer: (Header(msg))[] + foreach { msg.headers.push_back($$); } + : NewLine; +}; + +type Chunk = unit(inout body: Body) { + length: HexInteger &convert=$$.to_uint(16); + : OptionalWhiteSpace; + extension: RestOfLine; + : NewLine; + : bytes &size=self.length &eod &chunked -> body.data; + : NewLine if ( self.length != 0 ); +}; diff --git a/spicy/lib/spicy-driver-host.cc b/spicy/lib/spicy-driver-host.cc new file mode 100644 index 000000000..9a653837c --- /dev/null +++ b/spicy/lib/spicy-driver-host.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +/** + * Stripped down version of spicy-driver that includes just the pieces to + * drive already compiled parsers at runtime (rather than first compiling + * them itself). This can be compiled along with the C++ source code of those + * parsers to yield an final executable. + */ + +#include +#include +#include + +#include +#include + +using spicy::rt::fmt; + +static struct option long_driver_options[] = {{"abort-on-exceptions", required_argument, nullptr, 'A'}, + {"help", no_argument, nullptr, 'h'}, + {"increment", required_argument, nullptr, 'i'}, + {"list-parsers", no_argument, nullptr, 'l'}, + {"parser", required_argument, nullptr, 'p'}, + {"show-backtraces", required_argument, nullptr, 'B'}, + {"version", no_argument, nullptr, 'v'}, + {nullptr, 0, nullptr, 0}}; + +static void fatalError(const std::string& msg) { + fprintf(stderr, "spicy-driver: %s\n", msg.c_str()); + exit(1); +} + +class SpicyDriver : public spicy::rt::Driver { +public: + SpicyDriver() = default; + + void parseOptions(int argc, char** argv); + void usage(); + + bool opt_abort_on_exceptions = false; + bool opt_list_parsers = false; + bool opt_show_backtraces = false; + int opt_increment = 0; + std::string opt_parser; +}; + +void SpicyDriver::usage() { + std::cerr + << "Usage: cat | spicy-driver [options]\n" + "\n" + "Options:\n" + "\n" + " -A | --abort-on-exceptions When executing compiled code, abort() instead of throwing HILTI " + "exceptions.\n" + " -B | --show-backtraces Include backtraces when reporting unhandled exceptions.\n" + " -i | --increment Feed data incrementenally in chunks of size n.\n" + " -l | --list-parsers List available parsers and exit.\n" + " -p | --parser Use parser to process input. Only neeeded if more than one parser " + "is available.\n" + " -v | --version Print version information.\n" + "\n"; +} + +void SpicyDriver::parseOptions(int argc, char** argv) { + while ( true ) { + int c = getopt_long(argc, argv, "ABhdlp:i:v", long_driver_options, nullptr); + + if ( c < 0 ) + break; + + switch ( c ) { + case 'A': opt_abort_on_exceptions = true; break; + case 'B': opt_show_backtraces = true; break; + case 'i': + opt_increment = atoi(optarg); /* NOLINT */ + break; + case 'l': opt_list_parsers = true; break; + case 'p': opt_parser = optarg; break; + case 'v': std::cerr << "spicy-driver v" << hilti::rt::version() << std::endl; exit(0); + case 'h': usage(); exit(0); + default: usage(); fatalError(fmt("option %c not supported", c)); + } + } + + if ( optind != argc ) + usage(); +} + +int main(int argc, char** argv) { + SpicyDriver driver; + + driver.parseOptions(argc, argv); + + auto config = hilti::rt::configuration::get(); + config.abort_on_exceptions = driver.opt_abort_on_exceptions; + config.show_backtraces = driver.opt_show_backtraces; + hilti::rt::configuration::set(config); + + try { + hilti::rt::init(); + spicy::rt::init(); + + if ( driver.opt_list_parsers ) + driver.listParsers(std::cout); + + else { + auto parser = driver.lookupParser(driver.opt_parser); + if ( ! parser ) + fatalError(parser.error()); + + std::ifstream in("/dev/stdin", std::ios::in | std::ios::binary); + + if ( ! in.is_open() ) + fatalError("cannot open stdin for reading"); + + driver.processInput(**parser, in, driver.opt_increment); + } + + hilti::rt::done(); + spicy::rt::done(); + + } catch ( const std::exception& e ) { + std::cerr << fmt("[fatal error] terminating with uncaught exception of type %s: %s", + hilti::rt::demangle(typeid(e).name()), e.what()) + << std::endl; + exit(1); + } +} diff --git a/spicy/lib/spicy.spicy b/spicy/lib/spicy.spicy new file mode 100644 index 000000000..cb437a27c --- /dev/null +++ b/spicy/lib/spicy.spicy @@ -0,0 +1,77 @@ +# Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +module spicy; + +# Note: Retain the formatting here, doc/scripts/autogen-spicy-lib is picking up on that. + +## Specifies an address' IP family. +public type AddressFamily = enum { + IPv4, # IP4 address + IPv6 # IPv6 address +} &cxxname="hilti::rt::AddressFamily"; + +## Captures the state of base64 encoding/decoding for the corresponding library functions. +public type Base64Stream = __library_type("spicy::rt::base64::Stream"); + +## Specifies the bit order for individual bit ranges inside a bitfield. +public type BitOrder = enum { + LSB0, # bits are interpreted as lowest-signficiant-bit coming first + MSB0 # bits are interpreted as most-signficiant-bit coming first +} &cxxname="hilti::rt::integer::BitOrder"; + +## Specifies byte order for data operations. +public type ByteOrder = enum { + Little, # data is in little-endian byte order + Big, # data is in big-endian byte order + Network, # data is in network byte order (same a big endian) + Host # data is in byte order of the host we are executing on +} &cxxname="hilti::rt::ByteOrder"; + +## Captures state for incremenental regular expression matching. +public type MatchState = __library_type("hilti::rt::regexp::MatchState"); + +## Specifies a transport-layer prtocool. +public type Protocol = enum { + TCP, + UDP, + ICMP +} &cxxname="hilti::rt::Protocol"; + +## Specifies the type of a real value. +public type RealType = enum { + IEEE754_Single, # single precision in IEEE754 format + IEEE754_Double # double precision in IEEE754 format +} &cxxname="hilti::rt::real::Type"; + +## Specifies the policy for a sink's reassembler when encountering overlapping data. +public type ReassemblerPolicy = enum { + First # take the original data & discard the new data +} &cxxname="spicy::rt::sink::ReassemblerPolicy"; + +## Specifies a side an operation should operate on. +public type Side = enum { + Left, # operate on left side + Right, # operate on right side + Both # operate on both sides +} &cxxname="hilti::rt::bytes::Side"; + +## Captures the state of gzip decompression the corresponding library functions. +public type ZlibStream = __library_type("spicy::rt::zlib::Stream"); + +## Decompresses a chunk of data through the given zlib stream. +public function zlib_decompress(inout stream_: ZlibStream, data: bytes) : bytes &cxxname="spicy::rt::zlib::decompress" ; + +## Finalizes a zlib stream used for decompression. +public function zlib_finish(inout stream_: ZlibStream) : bytes &cxxname="spicy::rt::zlib::finish"; + +## Encodes a stream of data into base64. +public function base64_encode(inout stream_: Base64Stream, data: bytes) : bytes &cxxname="spicy::rt::base64::encode" ; + +## Decodes a stream of base64 data back into the clear. +public function base64_decode(inout stream_: Base64Stream, data: bytes) : bytes &cxxname="spicy::rt::base64::decode" ; + +## Finalizes a base64 stream used for decoding or encoding. +public function base64_finish(inout stream_: Base64Stream) : bytes &cxxname="spicy::rt::base64::finish"; + +## Returns the current wall clock time. +public function current_time() : time &cxxname="hilti::rt::time::current_time"; diff --git a/spicy/lib/spicy_rt.hlt b/spicy/lib/spicy_rt.hlt new file mode 100644 index 000000000..5a23586c2 --- /dev/null +++ b/spicy/lib/spicy_rt.hlt @@ -0,0 +1,67 @@ +module spicy_rt { + +public type ParseError = exception &cxxname="::spicy::rt::ParseError"; +public type UnitAlreadyConnected = exception &cxxname="::spicy::rt::UnitAlreadyConnected"; + +# State stored inside a unit to allow connecting it to a sink. +public type SinkState = __library_type("::spicy::rt::sink::detail::State*"); + +# Type for a Sink instance. When adding methods here, extend the C++-side spicy::rt::Sink type as well. +public type Sink = struct { + method void close(); + method void connect(strong_ref<*> unit); + method void connect_filter(strong_ref<*> unit); + method void connect_mime_type(string mime_type); + method void connect_mime_type(bytes mime_type); + method void gap(uint<64> seq, uint<64> len); + method uint<64> sequence_number(); + method void set_auto_trim(bool enable); + method void set_initial_sequence_number(uint<64> seq); + method void set_policy(any policy); + method uint<64> size(); + method void skip(uint<64> seq); + method void trim(uint<64> seq); + method void write(bytes data, optional> seq = Null, optional> len = Null); +} &cxxname="::spicy::rt::Sink"; + +public type HiltiResumable = __library_type("::hilti::rt::Resumable"); + +public type Filters = __library_type("::spicy::rt::filter::detail::Filters"); +public type Forward = __library_type("::spicy::rt::filter::detail::Forward"); + +declare public strong_ref filter_init(inout any unit, inout value_ref data, view cur) &cxxname="::spicy::rt::filter::init"; +declare public void filter_connect(inout any unit, strong_ref<*> filter) &cxxname="::spicy::rt::filter::connect"; +declare public void filter_disconnect(inout any unit) &cxxname="::spicy::rt::filter::disconnect"; +declare public void filter_forward(inout any filter, bytes b) &cxxname="::spicy::rt::filter::forward"; +declare public void filter_forward_eod(inout any filter) &cxxname="::spicy::rt::filter::forward_eod"; + +# Type for a parser definition. When making changes, adapt the C++-side +# spicy::rt::Parser as well. +public type Parser = struct { + string name; + any parse1; + any parse2; + string description; + any mime_types; + vector ports; +} &cxxname="::spicy::rt::Parser"; + +public type MIMEType = __library_type("::spicy::rt::MIMEType"); + +declare public void registerParser(inout Parser parse_func, any instance) &cxxname="::spicy::rt::detail::registerParser"; +declare public void printParserState(string unit_id, value_ref data, view cur, int<64> lahead, iterator lahead_end, string literal_mode, bool trim) &cxxname="::spicy::rt::detail::printParserState"; + +declare public bool waitForInputOrEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForInputOrEod"; +declare public bool waitForInputOrEod(inout value_ref data, view cur, uint<64> n, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForInputOrEod"; +declare public void waitForInput(inout value_ref data, view cur, string error_msg, string location, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForInput"; +declare public void waitForInput(inout value_ref data, view cur, uint<64> n, string error_msg, string location, strong_ref filters) &cxxname="::spicy::rt::detail::waitForInput"; +declare public bool waitForEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForEod"; + +declare public bool atEod(inout value_ref data, view cur) &cxxname="::spicy::rt::detail::atEod"; +declare public bool haveEod(inout value_ref data, view cur) &cxxname="::spicy::rt::detail::haveEod"; + +public type BitOrder = enum { LSB0, MSB0 } &cxxname="hilti::rt::integer::BitOrder"; + # TODO: Should accept BitOrder instead of enum<*> here. +declare public uint<*> extractBits(uint<*> v, uint<64> lower, uint<64> upper, enum<*> order) &cxxname="::hilti::rt::integer::bits"; + +} diff --git a/spicy/src/3rdparty/libb64/AUTHORS b/spicy/src/3rdparty/libb64/AUTHORS new file mode 100644 index 000000000..af6873756 --- /dev/null +++ b/spicy/src/3rdparty/libb64/AUTHORS @@ -0,0 +1,7 @@ +libb64: Base64 Encoding/Decoding Routines +====================================== + +Authors: +------- + +Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com diff --git a/spicy/src/3rdparty/libb64/BENCHMARKS b/spicy/src/3rdparty/libb64/BENCHMARKS new file mode 100644 index 000000000..fd34056e7 --- /dev/null +++ b/spicy/src/3rdparty/libb64/BENCHMARKS @@ -0,0 +1,85 @@ +-- Intro + +Some people have expressed opinions about how +fast libb64's encoding and decoding routines +are, as compared to some other BASE64 packages +out there. + +This document shows the result of a short and sweet +benchmark, which takes a large-ish file and +encodes/decodes it a number of times. +The winner is the executable that does this task the quickest. + +-- Platform + +The tests were all run on a Fujitsu-Siemens laptop, +with a Pentium M processor running at 2GHz, with +1GB of RAM, running Ubuntu 10.4. + +-- Packages + +The following BASE64 packages were used in this benchmark: + +- libb64-1.2 (libb64-base64) + From libb64.sourceforge.net + Size of executable: 18808 bytes + Compiled with: + CFLAGS += -O3 + BUFFERSIZE = 16777216 + +- base64-1.5 (fourmilab-base64) + From http://www.fourmilab.ch/webtools/base64/ + Size of executable: 20261 bytes + Compiled with Default package settings + +- coreutils 7.4-2ubuntu2 (coreutils-base64) + From http://www.gnu.org/software/coreutils/ + Size of executable: 38488 bytes + Default binary distributed with Ubuntu 10.4 + +-- Input File + +Using blender-2.49b-linux-glibc236-py25-i386.tar.bz2 +from http://www.blender.org/download/get-blender/ +Size: 18285329 bytes +(approx. 18MB) + +-- Method + +Encode and Decode the Input file 50 times in a loop, +using a simple shell script, and get the running time. + +-- Results + +$ time ./benchmark-libb64.sh +real 0m28.389s +user 0m14.077s +sys 0m12.309s + +$ time ./benchmark-fourmilab.sh +real 1m43.160s +user 1m23.769s +sys 0m8.737s + +$ time ./benchmark-coreutils.sh +real 0m36.288s +user 0m24.746s +sys 0m8.181s + +28.389 for 18MB * 50 += 28.389 for 900 + +-- Conclusion + +libb64 is the fastest encoder/decoder, and +has the smallest executable size. + +On average it will encode and decode at roughly 31.7MB/second. + +The closest "competitor" is base64 from GNU coreutils, which +reaches only 24.8MB/second. + +-- +14/06/2010 +chris.venter@gmail.com + diff --git a/spicy/src/3rdparty/libb64/CHANGELOG b/spicy/src/3rdparty/libb64/CHANGELOG new file mode 100644 index 000000000..6fd1c1764 --- /dev/null +++ b/spicy/src/3rdparty/libb64/CHANGELOG @@ -0,0 +1,25 @@ +libb64: Base64 Encoding/Decoding Routines +====================================== + +## Changelog ## + +Version 1.2 Release +------------------- +Removed the b64dec, b64enc, encoder and decoder programs in favour of +a better example, called base64, which encodes and decodes +depending on its arguments. + +Created a solution for Microsoft Visual Studio C++ Express 2010 +edition, which simply builds the base64 example as a console application. + +Version 1.1 Release +------------------- +Modified encode.h to (correctly) read from the iostream argument, +instead of std::cin. +Thanks to Peter K. Lee for the heads-up. + +No API changes. + +Version 1.0 Release +------------------- +The current content is the changeset. diff --git a/spicy/src/3rdparty/libb64/INSTALL b/spicy/src/3rdparty/libb64/INSTALL new file mode 100644 index 000000000..b05f5db69 --- /dev/null +++ b/spicy/src/3rdparty/libb64/INSTALL @@ -0,0 +1,44 @@ +libb64: Base64 Encoding/Decoding Routines +====================================== + +Requirements: +------------ +This piece of software has minimal requirements. + +I have tested it on the following systems: + +- a Linux machine, with the following specs: +(this was the original development machine) + * FedoraCore 4 + * kernel v. 2.6.11 (stock FC4 kernel) + * gcc version 4.0.1 20050727 (Red Hat 4.0.1-5) + * glibc-2.3.5-10 + * make v. 3.80 + * some arb version of makedepend + +- Windows XP machine + * MSYS 1.0 + * MinGW 5.1.4 + * gcc version 3.4.5 (mingw-vista special r3) + +- Windows XP machine (same as above) + * Microsoft Visual Studio 2010, Version 10.0.30319.1 RTMRel + +Barring any serious screwups on my part, this code should compile and run sweetly +under Cygwin and other systems too. If you DO get it running under some weird arch/os setup, +send me a mail, please. + +Compiling: +--------- +There is no configure. It would be overkill for something so simple... +Run make in the root directory. + +Installing: +---------- +Since the current targets are a standalone executable and a static library +(fancy name for archive) with some headers, an install script has not been implemented yet. +Simply copy the executable into your path, and use it. + +-- +peace out +Chris diff --git a/spicy/src/3rdparty/libb64/LICENSE b/spicy/src/3rdparty/libb64/LICENSE new file mode 100644 index 000000000..e27ef04fe --- /dev/null +++ b/spicy/src/3rdparty/libb64/LICENSE @@ -0,0 +1,29 @@ +Copyright-Only Dedication (based on United States law) +or Public Domain Certification + +The person or persons who have associated work with this document (the +"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of +his knowledge, the work of authorship identified is in the public domain of the +country from which the work is published, or (b) hereby dedicates whatever +copyright the dedicators holds in the work of authorship identified below (the +"Work") to the public domain. A certifier, moreover, dedicates any copyright +interest he may have in the associated work, and for these purposes, is +described as a "dedicator" below. + +A certifier has taken reasonable steps to verify the copyright status of this +work. Certifier recognizes that his good faith efforts may not shield him from +liability if in fact the work certified is not in the public domain. + +Dedicator makes this dedication for the benefit of the public at large and to +the detriment of the Dedicator's heirs and successors. Dedicator intends this +dedication to be an overt act of relinquishment in perpetuity of all present +and future rights under copyright law, whether vested or contingent, in the +Work. Dedicator understands that such relinquishment of all rights includes +the relinquishment of all rights to enforce (by lawsuit or otherwise) those +copyrights in the Work. + +Dedicator recognizes that, once placed in the public domain, the Work may be +freely reproduced, distributed, transmitted, used, modified, built upon, or +otherwise exploited by anyone for any purpose, commercial or non-commercial, +and in any way, including by methods that have not yet been invented or +conceived. diff --git a/spicy/src/3rdparty/libb64/Makefile b/spicy/src/3rdparty/libb64/Makefile new file mode 100644 index 000000000..fc413107f --- /dev/null +++ b/spicy/src/3rdparty/libb64/Makefile @@ -0,0 +1,25 @@ +all: all_src all_base64 + +all_src: + $(MAKE) -C src +all_base64: all_src + $(MAKE) -C base64 + +clean: clean_src clean_base64 clean_include + rm -f *~ *.bak + +clean_include: + rm -f include/b64/*~ + +clean_src: + $(MAKE) -C src clean; +clean_base64: + $(MAKE) -C base64 clean; + +distclean: clean distclean_src distclean_base64 + +distclean_src: + $(MAKE) -C src distclean; +distclean_base64: + $(MAKE) -C base64 distclean; + diff --git a/spicy/src/3rdparty/libb64/README b/spicy/src/3rdparty/libb64/README new file mode 100644 index 000000000..be93b7b42 --- /dev/null +++ b/spicy/src/3rdparty/libb64/README @@ -0,0 +1,138 @@ +b64: Base64 Encoding/Decoding Routines +====================================== + +Overview: +-------- +libb64 is a library of ANSI C routines for fast encoding/decoding data into and +from a base64-encoded format. C++ wrappers are included, as well as the source +code for standalone encoding and decoding executables. + +base64 consists of ASCII text, and is therefore a useful encoding for storing +binary data in a text file, such as xml, or sending binary data over text-only +email. + +References: +---------- +* Wikipedia article: + http://en.wikipedia.org/wiki/Base64 +* base64, another implementation of a commandline en/decoder: + http://www.fourmilab.ch/webtools/base64/ + +Why? +--- +I did this because I need an implementation of base64 encoding and decoding, +without any licensing problems. Most OS implementations are released under +either the GNU/GPL, or a BSD-variant, which is not what I require. + +Also, the chance to actually use the co-routine implementation in code is rare, +and its use here is fitting. I couldn't pass up the chance. +For more information on this technique, see "Coroutines in C", by Simon Tatham, +which can be found online here: +http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + +So then, under which license do I release this code? On to the next section... + +License: +------- +This work is released under into the Public Domain. +It basically boils down to this: I put this work in the public domain, and you +can take it and do whatever you want with it. + +An example of this "license" is the Creative Commons Public Domain License, a +copy of which can be found in the LICENSE file, and also online at +http://creativecommons.org/licenses/publicdomain/ + +Commandline Use: +--------------- +There is a new executable available, it is simply called base64. +It can encode and decode files, as instructed by the user. + +To encode a file: +$ ./base64 -e filea fileb +fileb will now be the base64-encoded version of filea. + +To decode a file: +$ ./base64 -d fileb filec +filec will now be identical to filea. + +Programming: +----------- +Some C++ wrappers are provided as well, so you don't have to get your hands +dirty. Encoding from standard input to standard output is as simple as + + #include + #include + int main() + { + base64::encoder E; + E.encode(std::cin, std::cout); + return 0; + } + +Both standalone executables and a static library is provided in the package, + +Implementation: +-------------- +It is DAMN fast, if I may say so myself. The C code uses a little trick which +has been used to implement coroutines, of which one can say that this +implementation is an example. + +(To see how the libb64 codebase compares with some other BASE64 implementations +available, see the BENCHMARKS file) + +The trick involves the fact that a switch-statement may legally cross into +sub-blocks. A very thorough and enlightening essay on co-routines in C, using +this method, can be found in the above mentioned "Coroutines in C", by Simon +Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html + +For example, an RLE decompressing routine, adapted from the article: +1 static int STATE = 0; +2 static int len, c; +3 switch (STATE) +4 { +5 while (1) +6 { +7 c = getchar(); +8 if (c == EOF) return EOF; +9 if (c == 0xFF) { +10 len = getchar(); +11 c = getchar(); +12 while (len--) +13 { +14 STATE = 0; +15 return c; +16 case 0: +17 } +18 } else +19 STATE = 1; +20 return c; +21 case 1: +22 } +23 } +24 } + +As can be seen from this example, a coroutine depends on a state variable, +which it sets directly before exiting (lines 14 and 119). The next time the +routine is entered, the switch moves control to the specific point directly +after the previous exit (lines 16 and 21).hands + +(As an aside, in the mentioned article the combination of the top-level switch, +the various setting of the state, the return of a value, and the labelling of +the exit point is wrapped in #define macros, making the structure of the +routine even clearer.) + +The obvious problem with any such routine is the static keyword. +Any static variables in a function spell doom for multithreaded applications. +Also, in situations where this coroutine is used by more than one other +coroutines, the consistency is disturbed. + +What is needed is a structure for storing these variabled, which is passed to +the routine seperately. This obviously breaks the modularity of the function, +since now the caller has to worry about and care for the internal state of the +routine (the callee). This allows for a fast, multithreading-enabled +implementation, which may (obviously) be wrapped in a C++ object for ease of +use. + +The base64 encoding and decoding functionality in this package is implemented +in exactly this way, providing both a high-speed high-maintanence C interface, +and a wrapped C++ which is low-maintanence and only slightly less performant. diff --git a/spicy/src/3rdparty/libb64/TODO b/spicy/src/3rdparty/libb64/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/spicy/src/3rdparty/libb64/base64/Makefile b/spicy/src/3rdparty/libb64/base64/Makefile new file mode 100644 index 000000000..30a2c5c03 --- /dev/null +++ b/spicy/src/3rdparty/libb64/base64/Makefile @@ -0,0 +1,56 @@ +BINARIES = base64 + +# Build flags (uncomment one) +############################# +# Release build flags +CFLAGS += -O3 +############################# +# Debug build flags +#CFLAGS += -g +############################# + +# select a buffersize +# a larger size should be faster, but takes more runtime memory +#BUFFERSIZE = 4096 +#BUFFERSIZE = 65536 +BUFFERSIZE = 16777216 + +SOURCES = base64.cc + +TARGETS = $(BINARIES) + +LINK.o = g++ + +CFLAGS += -Werror -pedantic +CFLAGS += -DBUFFERSIZE=$(BUFFERSIZE) +CFLAGS += -I../include + +CXXFLAGS += $(CFLAGS) + +vpath %.h ../include/b64 +vpath %.a ../src + +.PHONY : clean + +all: $(TARGETS) #strip + +base64: libb64.a + +strip: + strip $(BINARIES) *.exe + +clean: clean_VisualStudioProject + rm -f *.exe* *.o $(TARGETS) *.bak *~ +clean_VisualStudioProject: + $(MAKE) -C VisualStudioProject clean + +distclean: clean distclean_VisualStudioProject + rm -f depend +distclean_VisualStudioProject: clean_VisualStudioProject + $(MAKE) -C VisualStudioProject distclean + +depend: $(SOURCES) + makedepend -f- $(CFLAGS) $(SOURCES) 2> /dev/null 1> depend + +-include depend + diff --git a/spicy/src/3rdparty/libb64/base64/VisualStudioProject/Makefile b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/Makefile new file mode 100644 index 000000000..83aea868c --- /dev/null +++ b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/Makefile @@ -0,0 +1,11 @@ +DEBRIS = base64.sdf base64.suo base64.vcxproj.user + +all: + +clean: + rm -rf Debug Release + +distclean: clean + rm -f $(DEBRIS) + + diff --git a/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.sln b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.sln new file mode 100644 index 000000000..b357a9c00 --- /dev/null +++ b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base64", "base64.vcxproj", "{0B094121-DC64-4D74-AFA0-750B83F800D0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0B094121-DC64-4D74-AFA0-750B83F800D0}.Debug|Win32.ActiveCfg = Debug|Win32 + {0B094121-DC64-4D74-AFA0-750B83F800D0}.Debug|Win32.Build.0 = Debug|Win32 + {0B094121-DC64-4D74-AFA0-750B83F800D0}.Release|Win32.ActiveCfg = Release|Win32 + {0B094121-DC64-4D74-AFA0-750B83F800D0}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.vcxproj b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.vcxproj new file mode 100644 index 000000000..2110a9806 --- /dev/null +++ b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.vcxproj @@ -0,0 +1,92 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0B094121-DC64-4D74-AFA0-750B83F800D0} + Win32Proj + base64 + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + BUFFERSIZE=16777216;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + H:\builds\libb64\working.libb64\include;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + BUFFERSIZE=16777216;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + H:\builds\libb64\working.libb64\include;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + + + + + + + + + + + + + diff --git a/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.vcxproj.filters b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.vcxproj.filters new file mode 100644 index 000000000..f057197ba --- /dev/null +++ b/spicy/src/3rdparty/libb64/base64/VisualStudioProject/base64.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {12b9f2a2-b899-409a-a507-8cefe9c39b25} + + + {ce5598bd-67f3-430f-890b-cefa880e9405} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + diff --git a/spicy/src/3rdparty/libb64/base64/base64.cc b/spicy/src/3rdparty/libb64/base64/base64.cc new file mode 100644 index 000000000..acab41622 --- /dev/null +++ b/spicy/src/3rdparty/libb64/base64/base64.cc @@ -0,0 +1,94 @@ +/* +base64.cc - c++ source to a base64 reference encoder and decoder + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include +#include + +#include +#include +#include + +#include + +// Function which prints the usage of this executable +void usage() +{ + std::cerr<< \ + "base64: Encodes and Decodes files using base64\n" \ + "Usage: base64 [-e|-d] [input] [output]\n" \ + " Where [-e] will encode the input file into the output file,\n" \ + " [-d] will decode the input file into the output file, and\n" \ + " [input] and [output] are the input and output files, respectively.\n"; +} +// Function which prints the usage of this executable, plus a short message +void usage(const std::string& message) +{ + usage(); + std::cerr<<"Incorrect invocation of base64:\n"; + std::cerr< + +namespace base64 +{ + extern "C" + { + #include "cdecode.h" + } + + struct decoder + { + base64_decodestate _state; + int _buffersize; + + decoder(int buffersize_in = BUFFERSIZE) + : _buffersize(buffersize_in) + {} + + int decode(char value_in) + { + return base64_decode_value(value_in); + } + + int decode(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_decode_block(code_in, length_in, plaintext_out, &_state); + } + + void decode(std::istream& istream_in, std::ostream& ostream_in) + { + base64_init_decodestate(&_state); + // + const int N = _buffersize; + char* code = new char[N]; + char* plaintext = new char[N]; + int codelength; + int plainlength; + + do + { + istream_in.read((char*)code, N); + codelength = istream_in.gcount(); + plainlength = decode(code, codelength, plaintext); + ostream_in.write((const char*)plaintext, plainlength); + } + while (istream_in.good() && codelength > 0); + // + base64_init_decodestate(&_state); + + delete [] code; + delete [] plaintext; + } + }; + +} // namespace base64 + + + +#endif // BASE64_DECODE_H + diff --git a/spicy/src/3rdparty/libb64/include/b64/encode.h b/spicy/src/3rdparty/libb64/include/b64/encode.h new file mode 100644 index 000000000..5d807d971 --- /dev/null +++ b/spicy/src/3rdparty/libb64/include/b64/encode.h @@ -0,0 +1,77 @@ +// :mode=c++: +/* +encode.h - c++ wrapper for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ +#ifndef BASE64_ENCODE_H +#define BASE64_ENCODE_H + +#include + +namespace base64 +{ + extern "C" + { + #include "cencode.h" + } + + struct encoder + { + base64_encodestate _state; + int _buffersize; + + encoder(int buffersize_in = BUFFERSIZE) + : _buffersize(buffersize_in) + {} + + int encode(char value_in) + { + return base64_encode_value(value_in); + } + + int encode(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_encode_block(code_in, length_in, plaintext_out, &_state); + } + + int encode_end(char* plaintext_out) + { + return base64_encode_blockend(plaintext_out, &_state); + } + + void encode(std::istream& istream_in, std::ostream& ostream_in) + { + base64_init_encodestate(&_state); + // + const int N = _buffersize; + char* plaintext = new char[N]; + char* code = new char[2*N]; + int plainlength; + int codelength; + + do + { + istream_in.read(plaintext, N); + plainlength = istream_in.gcount(); + // + codelength = encode(plaintext, plainlength, code); + ostream_in.write(code, codelength); + } + while (istream_in.good() && plainlength > 0); + + codelength = encode_end(code); + ostream_in.write(code, codelength); + // + base64_init_encodestate(&_state); + + delete [] code; + delete [] plaintext; + } + }; + +} // namespace base64 + +#endif // BASE64_ENCODE_H + diff --git a/spicy/src/3rdparty/libb64/src/Makefile b/spicy/src/3rdparty/libb64/src/Makefile new file mode 100644 index 000000000..28b238257 --- /dev/null +++ b/spicy/src/3rdparty/libb64/src/Makefile @@ -0,0 +1,43 @@ +LIBRARIES = libb64.a + +# Build flags (uncomment one) +############################# +# Release build flags +CFLAGS += -O3 +############################# +# Debug build flags +#CFLAGS += -g +############################# + +SOURCES = cdecode.c cencode.c + +TARGETS = $(LIBRARIES) + +LINK.o = gcc + +CFLAGS += -Werror -pedantic +CFLAGS += -I../include + +vpath %.h ../include/b64 + +.PHONY : clean + +all: $(TARGETS) #strip + +libb64.a: cencode.o cdecode.o + $(AR) $(ARFLAGS) $@ $^ + +strip: + strip $(BINARIES) *.exe + +clean: + rm -f *.exe* *.o $(TARGETS) *.bak *~ + +distclean: clean + rm -f depend + +depend: $(SOURCES) + makedepend -f- $(CFLAGS) $(SOURCES) 2> /dev/null 1> depend + +-include depend + diff --git a/spicy/src/3rdparty/libb64/src/cdecode.c b/spicy/src/3rdparty/libb64/src/cdecode.c new file mode 100644 index 000000000..cd712c79a --- /dev/null +++ b/spicy/src/3rdparty/libb64/src/cdecode.c @@ -0,0 +1,88 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "../include/b64/cdecode.h" + +int base64_decode_value(char value_in) +{ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,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,-1,-1,-1,-1,-1,-1,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}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + while (1) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} + diff --git a/spicy/src/3rdparty/libb64/src/cencode.c b/spicy/src/3rdparty/libb64/src/cencode.c new file mode 100644 index 000000000..072517372 --- /dev/null +++ b/spicy/src/3rdparty/libb64/src/cencode.c @@ -0,0 +1,109 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "../include/b64/cencode.h" + +const int CHARS_PER_LINE = 72; + +void base64_init_encodestate(base64_encodestate* state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) +{ + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + while (1) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE/4) + { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ + char* codechar = code_out; + + switch (state_in->step) + { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = '\n'; + + return codechar - code_out; +} + diff --git a/spicy/src/3rdparty/libb64/src/depend b/spicy/src/3rdparty/libb64/src/depend new file mode 100644 index 000000000..e69de29bb diff --git a/spicy/src/ast/types/bitfield.cc b/spicy/src/ast/types/bitfield.cc new file mode 100644 index 000000000..25814c2fc --- /dev/null +++ b/spicy/src/ast/types/bitfield.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace spicy; + +Type type::bitfield::Bits::type() const { + if ( auto a = AttributeSet::find(attributes(), "&convert") ) + return hilti::type::Computed(*a->valueAs(), meta()); + + return hilti::type::UnsignedInteger(_field_width); +} + +Type type::Bitfield::type() const { + std::vector> elems; + + for ( const auto& b : bits() ) + elems.emplace_back(b.id(), b.type()); + + return type::Tuple(std::move(elems), meta()); +} + +std::optional type::Bitfield::bitsIndex(const ID& id) const { + for ( const auto& [i, b] : util::enumerate(bits()) ) { + if ( id == b.id() ) + return i; + } + + return {}; +} + +std::optional type::Bitfield::bits(const ID& id) const { + for ( const auto& b : bits() ) { + if ( id == b.id() ) + return b; + } + + return {}; +} diff --git a/spicy/src/ast/types/unit-items/field.cc b/spicy/src/ast/types/unit-items/field.cc new file mode 100644 index 000000000..efd4fe861 --- /dev/null +++ b/spicy/src/ast/types/unit-items/field.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include + +using namespace spicy; +using namespace spicy::detail; + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + explicit Visitor(const type::unit::item::Field& field, bool want_item_type) + : field(field), want_item_type(want_item_type) {} + + const type::unit::item::Field& field; + bool want_item_type; + + result_t operator()(const type::Bitfield& t) { return want_item_type ? t.type() : t; } + + result_t operator()(const hilti::type::RegExp& /* t */) { return hilti::type::Bytes(); } +}; + +} // namespace + +static Type _adaptType(const type::unit::item::Field& field, const Type& t, bool want_item_type) { + if ( auto e = Visitor(field, want_item_type).dispatch(t) ) + return std::move(*e); + + return type::effectiveType(t); +} + +Type spicy::type::unit::item::Field::parseType() const { return _adaptType(*this, _originalType(), false); } + +Type spicy::type::unit::item::Field::itemType() const { + if ( isContainer() ) { + auto itype = _adaptType(*this, parseType().as().elementType(), true); + return type::Vector(itype, itype.meta()); + } + + if ( auto a = AttributeSet::find(attributes(), "&convert") ) + return hilti::type::Computed(*a->valueAs(), meta()); + + return _adaptType(*this, _originalType(), true); +} diff --git a/spicy/src/ast/types/unit-items/switch.cc b/spicy/src/ast/types/unit-items/switch.cc new file mode 100644 index 000000000..c3c9f6958 --- /dev/null +++ b/spicy/src/ast/types/unit-items/switch.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace spicy; +using namespace spicy::detail; + +bool spicy::type::unit::item::Switch::hasNoFields() const { + for ( const auto& c : cases() ) { + for ( const auto& f : c.items() ) { + if ( ! f.itemType().isA() ) + return false; + } + } + + return true; +} + +std::optional spicy::type::unit::item::Switch::case_( + const type::unit::item::Field& x) { + for ( const auto& c : cases() ) { + for ( const auto& f : c.items() ) { + if ( f == x ) + return c; + } + } + + return {}; +} diff --git a/spicy/src/ast/types/unit.cc b/spicy/src/ast/types/unit.cc new file mode 100644 index 000000000..7055ea71b --- /dev/null +++ b/spicy/src/ast/types/unit.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace spicy; + +std::optional type::Unit::field(const ID& id) const { + for ( const auto& f : hilti::node::flattenedChilds(*this) ) { + if ( f.id() == id ) + return f; + } + + return {}; +} diff --git a/spicy/src/compiler/codegen/codegen.cc b/spicy/src/compiler/codegen/codegen.cc new file mode 100644 index 000000000..4c0785f8f --- /dev/null +++ b/spicy/src/compiler/codegen/codegen.cc @@ -0,0 +1,465 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace spicy; +using namespace spicy::detail; +using namespace spicy::detail::codegen; + +using util::fmt; + +namespace builder = hilti::builder; + +namespace { + +// Visitor that runs only once the 1st time AST transformation is triggered. +struct VisitorPassInit : public hilti::visitor::PreOrder { + VisitorPassInit(CodeGen* cg, hilti::Module* module) : cg(cg), module(module) {} + CodeGen* cg; + hilti::Module* module; + ID module_id = ID(""); + bool modified = false; + + std::vector> new_nodes; + + template + void replaceNode(position_t* p, T&& n) { + auto x = p->node; + p->node = std::forward(n); + p->node.setOriginalNode(module->preserve(x)); + modified = true; + } + + void finalize() { + if ( new_nodes.empty() ) + return; + + for ( auto& n : new_nodes ) + *n.first = std::move(n.second); + + new_nodes.clear(); + modified = true; + } + + void operator()(const hilti::declaration::Property& m) { cg->recordModuleProperty(m); } + + void operator()(const hilti::Module& m) { + module_id = m.id(); + cg->addDeclaration(builder::import("hilti", m.meta())); + cg->addDeclaration(builder::import("spicy_rt", m.meta())); + } + + void operator()(const hilti::declaration::Type& t, position_t p) { + auto u = t.type().tryAs(); + + if ( ! u ) + return; + + auto nu = type::setTypeID(*u, ID(module_id, t.id())).as(); + + if ( t.linkage() == declaration::Linkage::Public && ! nu.isPublic() ) + nu = type::Unit::setPublic(nu, true); + + // Create unit property items from global module items where the unit + // does not provide an overriding one. + std::vector ni; + for ( auto& p : cg->moduleProperties() ) { + if ( ! u->propertyItem(p.id()) ) + ni.emplace_back(type::unit::item::Property(p.id(), *p.expression(), true, p.meta())); + } + + if ( ni.size() ) + nu = type::Unit::addItems(nu, ni); + + auto nt = hilti::declaration::Type::setType(t, nu); + replaceNode(&p, nt); + + // Build the unit's grammar. + if ( auto r = cg->grammarBuilder()->run(nu, &p.node); ! r ) { + hilti::logger().error(r.error().description(), p.node.location()); + return; + } + + auto ns = cg->compileUnit(nu, false); + auto attrs = AttributeSet::add(t.attributes(), Attribute("&on-heap")); + auto nd = hilti::declaration::Type(nt.id(), ns, attrs, nt.linkage(), nt.meta()); + replaceNode(&p, nd); + } +}; + +// Visitor that runs multiple times whenever a transformation pass is triggered. +struct VisitorPassIterate : public hilti::visitor::PreOrder { + VisitorPassIterate(CodeGen* cg, hilti::Module* module) : cg(cg), module(module) {} + CodeGen* cg; + hilti::Module* module; + ID module_id = ID(""); + bool modified = false; + + template + void replaceNode(position_t* p, T&& n) { + auto x = p->node; + p->node = std::forward(n); + p->node.setOriginalNode(module->preserve(x)); + modified = true; + } + + Expression argument(const Expression& args, unsigned int i, std::optional def = {}) { + auto ctor = args.as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + auto value = ctor.as().value(); + + if ( i < value.size() ) + return ctor.as().value()[i]; + + if ( def ) + return *def; + + hilti::logger().internalError(fmt("missing argument %d", i)); + } + + void replaceNode(position_t* p, Node&& n) { + auto x = p->node; + + if ( x.location() && ! n.location() ) { + auto m = n.meta(); + m.setLocation(x.location()); + n.setMeta(std::move(m)); + } + + p->node = std::move(n); + p->node.setOriginalNode(module->preserve(x)); + modified = true; + } + + void operator()(const declaration::UnitHook& n, position_t p) { + const auto& unit_type = n.unitType(); + const auto& hook = n.unitHook().hook(); + + if ( ! unit_type ) + // Not resolved yet. + return; + + auto func = cg->compileHook(*unit_type, ID(*unit_type->typeID(), n.unitHook().id()), {}, hook.isForEach(), + hook.isDebug(), hook.type().parameters(), hook.body(), hook.priority(), n.meta()); + + replaceNode(&p, std::move(func)); + } + + result_t operator()(const operator_::bitfield::Member& n, position_t p) { + auto id = n.op1().as().id(); + auto idx = n.op0().type().as().bitsIndex(id); + assert(idx); + auto x = builder::index(n.op0(), *idx, n.meta()); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::unit::Offset& n, position_t p) { + auto begin = builder::deref(builder::member(n.op0(), ID("__begin"))); + auto cur = builder::deref(builder::member(n.op0(), ID("__position"))); + replaceNode(&p, builder::difference(cur, begin)); + } + + result_t operator()(const operator_::unit::Input& n, position_t p) { + auto begin = builder::deref(builder::member(n.op0(), ID("__begin"))); + replaceNode(&p, begin); + } + + result_t operator()(const operator_::unit::SetInput& n, position_t p) { + auto cur = builder::member(n.op0(), ID("__position_update")); + replaceNode(&p, builder::assign(cur, argument(n.op2(), 0))); + } + + result_t operator()(const operator_::unit::ConnectFilter& n, position_t p) { + auto x = builder::call("spicy_rt::filter_connect", {n.op0(), argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::unit::Forward& n, position_t p) { + auto x = builder::call("spicy_rt::filter_forward", {n.op0(), argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::unit::ForwardEod& n, position_t p) { + auto x = builder::call("spicy_rt::filter_forward_eod", {n.op0()}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::Close& n, position_t p) { + auto x = builder::memberCall(n.op0(), "close", {}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::Connect& n, position_t p) { + auto x = builder::memberCall(n.op0(), "connect", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::ConnectMIMETypeBytes& n, position_t p) { + auto x = builder::memberCall(n.op0(), "connect_mime_type", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::ConnectMIMETypeString& n, position_t p) { + auto x = builder::memberCall(n.op0(), "connect_mime_type", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::ConnectFilter& n, position_t p) { + auto x = builder::memberCall(n.op0(), "connect_filter", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::Gap& n, position_t p) { + auto x = builder::memberCall(n.op0(), "gap", {argument(n.op2(), 0), argument(n.op2(), 1)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::SequenceNumber& n, position_t p) { + auto x = builder::memberCall(n.op0(), "sequence_number", {}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::SetAutoTrim& n, position_t p) { + auto x = builder::memberCall(n.op0(), "set_auto_trim", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::SetInitialSequenceNumber& n, position_t p) { + auto x = builder::memberCall(n.op0(), "set_initial_sequence_number", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::SetPolicy& n, position_t p) { + auto x = builder::memberCall(n.op0(), "set_policy", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::SizeValue& n, position_t p) { + auto x = builder::memberCall(n.op0(), "size", {}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::SizeReference& n, position_t p) { + auto x = builder::memberCall(n.op0(), "size", {}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::Skip& n, position_t p) { + auto x = builder::memberCall(n.op0(), "skip", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::Trim& n, position_t p) { + auto x = builder::memberCall(n.op0(), "trim", {argument(n.op2(), 0)}); + replaceNode(&p, std::move(x)); + } + + result_t operator()(const operator_::sink::Write& n, position_t p) { + auto x = builder::memberCall(n.op0(), "write", + {argument(n.op2(), 0), argument(n.op2(), 1, builder::null()), + argument(n.op2(), 2, builder::null())}); + replaceNode(&p, std::move(x)); + } + + void operator()(const statement::Print& n, position_t p) { + auto exprs = n.expressions(); + + switch ( exprs.size() ) { + case 0: { + auto call = builder::call("hilti::print", {builder::string("")}); + replaceNode(&p, hilti::statement::Expression(call)); + break; + } + + case 1: { + auto call = builder::call("hilti::print", std::move(exprs)); + replaceNode(&p, hilti::statement::Expression(call)); + break; + } + + default: { + auto call = builder::call("hilti::printValues", {builder::tuple(std::move(exprs))}); + replaceNode(&p, hilti::statement::Expression(call)); + break; + } + } + } + + void operator()(const statement::Stop& n, position_t p) { + auto b = builder::Builder(cg->context()); + b.addAssign(builder::id("__stop"), builder::bool_(true), n.meta()); + b.addReturn(n.meta()); + replaceNode(&p, b.block()); + } + + void operator()(const type::ResolvedID& n, position_t p) { + // Some of the original name/type bindings may have become invalid, + // flag them to be resolved again. + + if ( n.isValid() ) + return; + + replaceNode(&p, builder::typeByID(n.id())); + } + + void operator()(const type::Sink& n, position_t p) { + // Strong reference (instead of value reference) so that copying unit + // instances doesn't copy the sink. + auto sink = hilti::type::StrongReference(builder::typeByID("spicy_rt::Sink", n.meta())); + replaceNode(&p, Type(sink)); + } +}; + + +} // anonymous namespace + +bool CodeGen::compileModule(hilti::Node* root, bool init, hilti::Unit* u) { + util::timing::Collector _("spicy/compiler/codegen"); + + _hilti_unit = u; + _root = root; + + bool modified = false; + + if ( init ) { + auto v = VisitorPassInit(this, &root->as()); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + v.finalize(); + + modified = (modified || v.modified); + + if ( hilti::logger().errors() ) + goto done; + + if ( _new_decls.size() ) { + for ( const auto& n : _new_decls ) + hiltiModule()->add(n); + + _new_decls.clear(); + modified = true; + } + } + + { + auto v = VisitorPassIterate(this, &root->as()); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + modified = (modified || v.modified); + } + +done: + _hilti_unit = nullptr; + _root = nullptr; + + return modified; +} + +std::optional CodeGen::compileHook( + const type::Unit& unit, const ID& id, std::optional> field, + bool foreach, bool debug, std::vector params, std::optional body, + std::optional priority, const hilti::Meta& meta) { + if ( debug && ! options().debug ) + return {}; + + std::optional item_type; + + if ( field ) { + if ( ! field->get().parseType().isA() ) + item_type = field->get().itemType(); + } + else { + // Try to locate field by ID. + if ( auto i = unit.field(id.local()) ) { + auto f = i->as(); + if ( ! f.parseType().isA() ) + item_type = f.itemType(); + } + } + + if ( foreach ) { + // We have no easy way to get to the resolved element type, so defer + // that until we can derive it manually. + auto ut = Type(hilti::type::UnresolvedID(*unit.typeID())); + + auto cb = [id](Node& n) -> Type { + auto t = type::effectiveType(n.as()); + + auto rt = t.tryAs(); + if ( ! rt ) + return type::unknown; + + auto st = rt->dereferencedType().tryAs(); + if ( ! st ) + return type::unknown; + + return st->field(id.local())->type().as().elementType(); + }; + + auto element_type = type::Computed(ut, cb); + params.push_back({ID("__dd"), element_type, hilti::type::function::parameter::Kind::In, {}}); + params.push_back({ID("__stop"), type::Bool(), hilti::type::function::parameter::Kind::InOut, {}}); + } + else if ( item_type ) + params.push_back({ID("__dd"), *item_type, hilti::type::function::parameter::Kind::In, {}}); + + auto rt = hilti::type::function::Result(hilti::type::Void()); + auto ft = hilti::type::Function(std::move(rt), params, hilti::type::function::Flavor::Hook, meta); + auto hid = fmt("__on_%s%s", id.local(), (foreach ? "_foreach" : "")); + + if ( ! id.namespace_().empty() ) + hid = fmt("%s::%s", id.namespace_(), hid); + + std::optional attrs; + + if ( priority ) + attrs = AttributeSet::add(attrs, Attribute("&priority", *priority)); + + auto f = hilti::Function(ID(hid), std::move(ft), std::move(body), hilti::function::CallingConvention::Standard, + std::move(attrs), meta); + return hilti::declaration::Function(std::move(f), hilti::declaration::Linkage::Struct, meta); +} + +hilti::Module* CodeGen::hiltiModule() const { + if ( ! _hilti_unit ) + hilti::logger().internalError("not compiling a HILTI unit"); + + return &_root->as(); +} + +hilti::Unit* CodeGen::hiltiUnit() const { + if ( ! _hilti_unit ) + hilti::logger().internalError("not compiling a HILTI unit"); + + return _hilti_unit; +} + +NodeRef CodeGen::preserveNode(Expression x) { return hiltiModule()->preserve(std::move(x)); } + +NodeRef CodeGen::preserveNode(Statement x) { return hiltiModule()->preserve(std::move(x)); } + +NodeRef CodeGen::preserveNode(Type x) { return hiltiModule()->preserve(std::move(x)); } diff --git a/spicy/src/compiler/codegen/grammar-builder.cc b/spicy/src/compiler/codegen/grammar-builder.cc new file mode 100644 index 000000000..75fdf71eb --- /dev/null +++ b/spicy/src/compiler/codegen/grammar-builder.cc @@ -0,0 +1,305 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace spicy; +using namespace spicy::detail; +using namespace spicy::detail::codegen; + +using util::fmt; + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + Visitor(codegen::GrammarBuilder* gb, Grammar* g) : gb(gb), grammar(g) {} + codegen::GrammarBuilder* gb; + Grammar* grammar; + + using CurrentField = std::pair&>; + std::vector fields; + util::Cache cache; + util::Uniquer uniquer; + + auto currentField() { return fields.back(); } + + void pushField(CurrentField f) { fields.emplace_back(f); } + + void popField() { fields.pop_back(); } + + bool haveField() { return ! fields.empty(); } + + std::optional productionForItem(std::reference_wrapper node) { + auto field = node.get().tryAs(); + if ( field ) + pushField({*field, node}); + + auto p = dispatch(node); + + if ( field ) + popField(); + + return p; + } + + Production productionForCtor(const Ctor& c, const ID& id) { + return production::Ctor(uniquer.get(id), c, c.meta().location()); + } + + Production productionForType(const Type& t, const ID& id) { + if ( auto prod = dispatch(hilti::type::effectiveType(t)) ) + return std::move(*prod); + + // Fallback: Just a plain type. + return production::Variable(uniquer.get(id), t, t.meta().location()); + } + + Production productionForLoop(Production sub, position_t p) { + const auto& loc = p.node.location(); + auto& field = currentField().first; + auto id = uniquer.get(field.id()); + auto count = AttributeSet::find(field.attributes(), "&count"); + auto size = AttributeSet::find(field.attributes(), "&size"); + auto until = AttributeSet::find(field.attributes(), "&until"); + auto until_including = AttributeSet::find(field.attributes(), "&until_including"); + auto while_ = AttributeSet::find(field.attributes(), "&while"); + auto repeat = field.repeatCount(); + + auto m = sub.meta(); + + if ( ! m.field() ) + m.setField(NodeRef(currentField().second), false); + + m.setContainer(NodeRef(currentField().second)); + sub.setMeta(std::move(m)); + + if ( repeat && ! repeat->type().isA() ) + return production::Counter(id, *repeat, sub, loc); + + if ( count ) + return production::Counter(id, *count->valueAs(), sub, loc); + + if ( size ) + // When parsing, our view will be limited to the specified input + // size, so just iterate until EOD. + return production::ForEach(id, sub, true, loc); + + if ( while_ || until || until_including ) + // The container parsing will evaluate the corresponding stop + // conditon. + return production::ForEach(id, sub, true, loc); + + // Nothing specified, use look-ahead to figure out when to stop + // parsing. + // + // Left-factored & right-recursive. + // + // List1 -> Item List2 + // List2 -> Epsilon | List1 + + auto x = production::Unresolved(); + auto l1 = production::LookAhead(id + "_l1", production::Epsilon(loc), x, loc); + auto l2 = production::Sequence(id + "_l2", {sub, l1}, loc); + grammar->resolve(&x, std::move(l2)); + + auto c = production::Enclosure(id, std::move(l1), loc); + auto me = c.meta(); + me.setField(NodeRef(currentField().second), false); + c.setMeta(std::move(me)); + return std::move(c); + } + + Production operator()(const spicy::type::unit::item::Field& n, position_t p) { + Production prod; + + if ( auto c = n.ctor() ) { + prod = productionForCtor(*c, n.id()); + + if ( n.itemType().isA() || n.itemType().isA() ) + prod = productionForLoop(prod, p); + } + else if ( n.vectorItem() ) { + auto sub = productionForItem(p.node.as().vectorItemNode()); + assert(sub); + prod = productionForLoop(std::move(*sub), p); + } + else + prod = productionForType(n.parseType(), n.id()); + + auto m = prod.meta(); + m.setField(NodeRef(currentField().second), true); + prod.setMeta(std::move(m)); + return prod; + } + + Production operator()(const spicy::type::unit::item::Switch& n, position_t p) { + auto productionForCase = [this](Node& c, const std::string& label) { + std::vector prods; + + for ( auto&& n : c.as().itemNodes() ) { + if ( auto prod = productionForItem(n) ) + prods.push_back(*prod); + } + + return production::Sequence(label, std::move(prods), c.meta().location()); + }; + + auto switch_sym = uniquer.get("switch"); + + if ( n.expression() ) { + // Switch based on value of expression. + production::Switch::Cases cases; + std::optional default_; + int i = 0; + + for ( auto&& n : p.node.as().casesNodes() ) { + auto c = n.get().as(); + + if ( c.isDefault() ) + default_ = productionForCase(n, fmt("%s_default", switch_sym)); + else { + auto prod = productionForCase(n, fmt("%s_case_%d", switch_sym, ++i)); + cases.emplace_back(c.expressions(), std::move(prod)); + } + } + + return production::Switch(switch_sym, *n.expression(), std::move(cases), std::move(default_), + n.meta().location()); + } + + else { + // Switch by look-ahead. + std::optional prev; + + int i = 0; + auto d = production::look_ahead::Default::None; + + for ( auto&& n : p.node.as().casesNodes() ) { + auto c = n.get().as(); + + Production prod; + + if ( c.isDefault() ) + prod = productionForCase(n, fmt("%s_default", switch_sym)); + else + prod = productionForCase(n, fmt("%s_case_%d", switch_sym, ++i)); + + if ( ! prev ) { + prev = prod; + + if ( c.isDefault() ) + d = production::look_ahead::Default::First; + + continue; + } + + if ( c.isDefault() ) + d = production::look_ahead::Default::Second; + + auto lah_sym = fmt("%s_lha_%d", switch_sym, i); + auto lah = production::LookAhead(lah_sym, std::move(*prev), std::move(prod), d, c.meta().location()); + prev = std::move(lah); + } + + return *prev; + } + } + + Production operator()(const hilti::declaration::Type& t) { return *dispatch(t.type()); } + + Production operator()(const type::Unit& n, position_t p) { + auto prod = cache.getOrCreate( + *n.typeID(), []() { return production::Unresolved(); }, + [&](auto& unresolved) { + auto id = uniquer.get(*n.typeID()); + + std::vector items; + + for ( auto n : p.node.as().nodesOfType() ) { + if ( auto p = productionForItem(n) ) + items.push_back(*p); + } + + std::vector args; + + if ( haveField() ) + args = currentField().first.arguments(); + + auto unit = production::Unit(id, n, std::move(args), std::move(items), n.meta().location()); + grammar->resolve(&unresolved.template as(), std::move(unit)); + return unresolved; + }); + + // Give this production its own meta instance. Due to the caching it + // would normally have a shared one. + // TODO(robin): Rename _setMetaInstance(), or give it clearMeta() or such. + prod._setMetaInstance(std::make_shared()); + return prod; + } + + Production operator()(const type::ResolvedID& n) { + auto t = (*n.ref()).as().type(); + auto x = dispatch(t); + assert(x); + return *x; + } + + Production operator()(const type::Struct& n, position_t /* p */) { + // Must be a unit that's already been converted. + assert(n.originalNode()); + auto x = dispatch(*n.originalNode()); + return *x; + } + + Production operator()(const type::ValueReference& n, position_t /* p */) { + // Forward to referenced type, which will usually be a unit. + auto x = dispatch(n.dereferencedType()); + assert(x); + return *x; + } + + Production operator()(const type::Vector& n, position_t p) { + auto sub = productionForType(n.elementType(), ID(fmt("%s", n.elementType()))); + return productionForLoop(std::move(sub), p); + } +}; + +} // anonymous namespace + +Result GrammarBuilder::run(const type::Unit& unit, Node* node) { + assert(unit.typeID()); + auto id = *unit.typeID(); + Grammar g(id, node->location()); + auto v = Visitor(this, &g); + + auto root = v.dispatch(node); + assert(root); + + g.setRoot(*root); + + if ( auto r = g.finalize(); ! r ) + return r.error(); + + if ( hilti::logger().isEnabled(spicy::logging::debug::Grammar) ) { + hilti::logging::Stream dbg(spicy::logging::debug::Grammar); + g.printTables(dbg, true); + } + + _grammars[id] = std::move(g); + return Nothing(); +} + +const Grammar& GrammarBuilder::grammar(const type::Unit& unit) { + if ( _grammars.find(*unit.typeID()) == _grammars.end() ) + hilti::logger().internalError(fmt("grammar for unit %s accessed before it's been computed", *unit.typeID()), + unit.meta().location()); + + return _grammars[*unit.typeID()]; +} diff --git a/spicy/src/compiler/codegen/grammar.cc b/spicy/src/compiler/codegen/grammar.cc new file mode 100644 index 000000000..a435d8825 --- /dev/null +++ b/spicy/src/compiler/codegen/grammar.cc @@ -0,0 +1,398 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace spicy; +using namespace spicy::detail; +using namespace spicy::detail::codegen; +using util::fmt; + +class UnknownReference : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +std::string Grammar::_productionLocation(const Production& p) const { + std::string loc; + + if ( ! _name.empty() ) { + loc += fmt("grammar %s", _name); + + if ( _location ) + loc += fmt(" (%s)", _location); + + loc += ", "; + } + + loc += fmt("production %s", p.symbol()); + + if ( p.location() ) + loc += fmt(" (%s)", p.location()); + + return loc; +} + +std::vector> Grammar::_rhss(const Production& p) { + std::vector> nrhss; + + for ( const auto& rhs : p.rhss() ) { + std::vector nrhs; + for ( const auto& r : rhs ) { + if ( auto x = r.tryAs() ) + nrhs.push_back(resolved(*x)); + else + nrhs.push_back(r); + } + nrhss.push_back(std::move(nrhs)); + } + + return nrhss; +} + +Result Grammar::setRoot(const Production& p) { + if ( _root ) + return hilti::result::Error("root production is already set"); + + auto symbol = p.symbol(); + + if ( symbol.empty() ) + return hilti::result::Error("root production must have a symbol"); + + _addProduction(p); + _root = std::move(symbol); + return Nothing(); +} + +void Grammar::resolve(production::Unresolved* r, Production p) { + _resolved[r->referencedSymbol()] = p.symbol(); + r->resolve(p.symbol()); + p._setMetaInstance(r->_metaInstance()); + _addProduction(p); +} + +const Production& Grammar::resolved(const production::Resolved& r) const { + if ( auto np = _resolved.find(r.referencedSymbol()); np != _resolved.end() ) + return _prods.at(np->second); + + throw UnknownReference(r.referencedSymbol()); +} + +Result Grammar::finalize() { + if ( ! _root ) + return hilti::result::Error("grammar does not have a root production"); + + _simplify(); + return _computeTables(); +} + +void Grammar::_addProduction(const Production& p) { + if ( p.symbol().empty() ) + return; + + if ( p.isA() ) + return; + + if ( _prods.find(p.symbol()) != _prods.end() ) + return; + + _prods.insert(std::make_pair(p.symbol(), p)); + + if ( p.isNonTerminal() ) { + _nterms.push_back(p.symbol()); + + for ( const auto& rhs : p.rhss() ) + for ( const auto& r : rhs ) + _addProduction(r); + } + + if ( p.isA() || p.isLiteral() ) + _needs_look_ahead = true; +} + +void Grammar::_simplify() { + // Remove unused productions. + + bool changed = true; + + while ( changed ) { + changed = false; + auto closure = _computeClosure(*root()); + + for ( const auto& p : util::set_difference(util::map_values(_prods), closure) ) { + _prods.erase(p.symbol()); + _nterms.erase(std::remove(_nterms.begin(), _nterms.end(), p.symbol()), _nterms.end()); + changed = true; + } + } +} + +std::set Grammar::_computeClosure(const Production& p) { + std::function&, const Production&)> closure = [&](auto& c, const auto& p) -> void { + if ( p.symbol().empty() || c.find(p) != c.end() ) + return; + + c.insert(p); + + if ( p.isTerminal() ) + return; + + for ( const auto& rhss : _rhss(p) ) + for ( const auto& rhs : rhss ) + closure(c, rhs); + }; + + std::set c; + closure(c, p); + return c; +} + +bool Grammar::_add(std::map>* tbl, const Production& dst, + const std::set& src, bool changed) { + const auto& idx = dst.symbol(); + auto t = tbl->find(idx); + assert(t != tbl->end()); + + auto set = t->second; + auto union_ = util::set_union(set, src); + + if ( union_.size() == set.size() ) + // All in there already. + return changed; + + (*tbl)[idx] = union_; + return true; +} + +bool Grammar::_isNullable(std::vector::const_iterator i, std::vector::const_iterator j) { + while ( i != j ) { + auto rhs = *i++; + + if ( rhs.isA() ) + continue; + + if ( rhs.isTerminal() ) + return false; + + if ( ! _nullable[rhs.symbol()] ) + return false; + } + + return true; +} + +std::set Grammar::_getFirst(const Production& p) { + if ( p.isA() ) + return {}; + + if ( p.isTerminal() ) + return {p.symbol()}; + + return _first[p.symbol()]; +} + +std::set Grammar::_getFirstOfRhs(const std::vector& rhs) { + auto first = std::set(); + + for ( const auto& p : rhs ) { + if ( p.isA() ) + continue; + + if ( p.isTerminal() ) + return {p.symbol()}; + + first = util::set_union(first, _first[p.symbol()]); + + if ( ! _nullable[p.symbol()] ) + break; + } + + return first; +} + +Result Grammar::_computeTables() { + // Computes FIRST, FOLLOW, & NULLABLE. This follows roughly the Algorithm + // 3.13 from Modern Compiler Implementation in C by Appel/Ginsburg. See + // http://books.google.com/books?id=A3yqQuLW5RsC&pg=PA49. + + // Initializde sets. + for ( const auto& sym : _nterms ) { + _nullable[sym] = false; + _first[sym] = {}; + _follow[sym] = {}; + } + + // SafeIterator until no further change. + while ( true ) { + bool changed = false; + + for ( const auto& sym : _nterms ) { + auto& p = _prods.find(sym)->second; + + for ( const auto& rhss : _rhss(p) ) { + auto first = rhss.begin(); + auto last = rhss.end(); + + if ( _isNullable(first, last) && ! _nullable[sym] ) { + _nullable[sym] = true; + changed = true; + } + + for ( auto i = first; i != last; i++ ) { + auto rhs = *i; + + if ( _isNullable(first, i) ) + changed = _add(&_first, p, _getFirst(rhs), changed); + + if ( ! rhs.isNonTerminal() ) + continue; + + auto next = i; + ++next; + + if ( _isNullable(next, last) ) + changed = _add(&_follow, rhs, _follow[sym], changed); + + for ( auto j = next; j != last; ++j ) { + if ( _isNullable(next, j) ) { + changed = _add(&_follow, rhs, _getFirst(*j), changed); + } + } + } + } + } + + if ( ! changed ) + break; + } + + // Build the look-ahead sets. + for ( auto& sym : _nterms ) { + auto& p = _prods.find(sym)->second; + + if ( ! p.isA() ) + continue; + + auto& lap = p.as(); + auto rhss = _rhss(p); + + assert(rhss.size() == 2); + auto r = rhss.begin(); + + auto laheads = std::vector>{{}, {}}; + + for ( auto i = 0; i < 2; ++i ) { + auto rhs = *r++; + + for ( const auto& term : _getFirstOfRhs(rhs) ) + laheads[i] = util::set_union(laheads[i], {term}); + + if ( _isNullable(rhs.begin(), rhs.end()) ) { + for ( const auto& term : _follow[sym] ) + laheads[i] = util::set_union(laheads[i], {term}); + } + } + + std::set v0; + std::set v1; + std::set lahs; + + for ( auto i = 0; i < 2; ++i ) { + for ( const auto& s : laheads[i] ) { + auto p = _prods.find(s); + assert(p != _prods.end()); + + if ( p->second.isNonTerminal() ) + return hilti::result::Error( + fmt("%s: look-ahead cannot depend on non-terminal", _productionLocation(p->second))); + + if ( i == 0 ) + v0.insert(p->second); + else + v1.insert(p->second); + } + } + + lap.setLookAheads(std::make_pair(std::move(v0), std::move(v1))); + } + + return _check(); +} + +Result Grammar::_check() { + for ( const auto& sym : _nterms ) { + auto& p = _prods.find(sym)->second; + + if ( ! p.isA() ) + continue; + + auto& lap = _prods.find(sym)->second.as(); + auto laheads = lap.lookAheads(); + + std::set syms1; + std::set syms2; + + for ( const auto& p : laheads.first ) + syms1.insert(p.render()); + + for ( const auto& p : laheads.second ) + syms2.insert(p.render()); + + if ( syms1.size() == 0 && syms2.size() == 0 ) + return hilti::result::Error( + fmt("no look-ahead symbol for either alternative in %s\n", _productionLocation(p))); + + auto isect = util::set_intersection(syms1, syms2); + + if ( isect.size() ) + return hilti::result::Error(fmt("%s is ambigious for look-ahead symbol(s) { %s }\n", _productionLocation(p), + util::join(isect, ", "))); + + for ( const auto& q : util::set_union(laheads.first, laheads.second) ) { + if ( ! q.isTerminal() ) + return hilti::result::Error( + fmt("%s: look-ahead cannot depend on non-terminal\n", _productionLocation(p))); + } + } + + return Nothing(); +} + +void Grammar::printTables(std::ostream& out, bool verbose) { + out << "=== Grammar " << _name << std::endl; + + for ( const auto& i : _prods ) { + std::string field; + + if ( const auto& f = i.second.meta().field() ) { + auto isfp = i.second.meta().isFieldProduction() ? " (*)" : ""; + field = + fmt(" [field: %s%s] [item-type: %s] [parse-type: %s]", f->id(), isfp, f->itemType(), f->parseType()); + } + + out << fmt(" %3s %s%s", (_root && i.first == _root ? "(*)" : ""), i.second, field) << std::endl; + } + + if ( ! verbose ) { + out << std::endl; + return; + } + + out << std::endl << " -- Epsilon:" << std::endl; + + for ( const auto& i : _nullable ) + out << fmt(" %s = %s", i.first, i.second) << std::endl; + + out << std::endl << " -- First_1:" << std::endl; + + for ( const auto& i : _first ) + out << fmt(" %s = { %s }", i.first, util::join(i.second, ", ")) << std::endl; + + out << std::endl << " -- Follow:" << std::endl; + + for ( const auto& i : _follow ) + out << fmt(" %s = { %s }", i.first, util::join(i.second, ", ")) << std::endl; + + out << std::endl; +} diff --git a/spicy/src/compiler/codegen/parser-builder.cc b/spicy/src/compiler/codegen/parser-builder.cc new file mode 100644 index 000000000..fc1d830dc --- /dev/null +++ b/spicy/src/compiler/codegen/parser-builder.cc @@ -0,0 +1,1221 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Enable visitor usage for Production. Order of includes is important here. +#include + +#include +#include + +#include + +using namespace spicy; +using namespace spicy::detail; +using namespace spicy::detail::codegen; +using util::fmt; + +namespace builder = hilti::builder; + +const hilti::Type look_ahead::Type = hilti::type::SignedInteger(64); // TODO(cppcoreguidelines-interfaces-global-init) +const hilti::Expression look_ahead::None = builder::integer(0); // TODO(cppcoreguidelines-interfaces-global-init) +const hilti::Expression look_ahead::Eod = builder::integer(-1); // TODO(cppcoreguidelines-interfaces-global-init) + +ParserState::ParserState(const type::Unit& unit, const Grammar& grammar, Expression data, Expression cur) + : unit(std::cref(unit)), + unit_id(*unit.typeID()), + needs_look_ahead(grammar.needsLookAhead()), + self(hilti::expression::UnresolvedID(ID("self"))), + data(std::move(data)), + cur(std::move(cur)) {} + +void ParserState::printDebug(const std::shared_ptr& builder) const { + builder->addCall("spicy_rt::printParserState", {builder::string(unit_id), data, cur, lahead, lahead_end, + builder::string(to_string(literal_mode)), trim}); +} + +namespace spicy::detail::codegen { + +struct ProductionVisitor + : public hilti::detail::visitor::Visitor { + ProductionVisitor(ParserBuilder* pb, const Grammar& g) : pb(pb), grammar(g) {} + auto cg() { return pb->cg(); } + auto state() { return pb->state(); } + void pushState(ParserState p) { pb->pushState(std::move(p)); } + auto popState() { return pb->popState(); } + + auto builder() { return pb->builder(); } + auto pushBuilder(std::shared_ptr b) { return pb->pushBuilder(std::move(b)); } + auto pushBuilder() { return pb->pushBuilder(); } + auto pushBuilder(std::shared_ptr b, const std::function& func) { + return pb->pushBuilder(std::move(b), func); + } + auto popBuilder() { return pb->popBuilder(); } + + auto destination() { return _destinations.back(); } + auto pushDestination(const std::optional& e) { _destinations.push_back(e); } + auto popDestination() { + auto back = _destinations.back(); + _destinations.pop_back(); + return back; + } + + ParserBuilder* pb; + const Grammar& grammar; + util::Cache parse_functions; + std::vector new_fields; + std::vector> _destinations = {{}}; + + void beginProduction(const Production& p) { + builder()->addComment(fmt("Begin parsing production: %s", util::trim(std::string(p))), + hilti::statement::comment::Separator::Before); + if ( pb->options().debug ) { + pb->state().printDebug(builder()); + builder()->addDebugMsg("spicy-verbose", fmt("- parsing production: %s", util::trim(std::string(p)))); + builder()->addCall("hilti::debugIndent", {builder::string("spicy-verbose")}); + } + + pb->saveParsePosition(); + } + + void endProduction(const Production& p) { + if ( pb->options().debug ) + builder()->addCall("hilti::debugDedent", {builder::string("spicy-verbose")}); + + builder()->addComment(fmt("End parsing production: %s", util::trim(std::string(p))), + hilti::statement::comment::Separator::After); + } + + // Returns a boolean expression that's 'true' if a 'stop' was encountered. + Expression _parseProduction(const Production& p, const production::Meta& meta, bool forwarding) { + const auto is_field_owner = + (meta.field() && meta.isFieldProduction() && ! p.isA() && ! meta.container()); + + if ( is_field_owner ) + preParseField(p, meta); + + beginProduction(p); + + std::optional container_element; + + if ( const auto& c = meta.container(); c && ! forwarding ) { + auto etype = + type::Computed(builder::memberCall(builder::member(builder::id("self"), c->id()), "front", {}), false); + container_element = builder()->addTmp("elem", etype); + pushDestination(container_element); + } + + if ( p.atomic() ) { + if ( auto x = dispatch(p); ! x ) + hilti::logger().internalError( + fmt("ParserBuilder: atomic production %s not handled (%s)", p.typename_(), p)); + } + else if ( auto unit = p.tryAs(); unit && *unit->unitType().typeID() != state().unit_id ) { + // Parsing a different unit type. We call the other unit's parse + // function, but don't have to create it here. + std::vector args = {pb->state().data, pb->state().cur, pb->state().trim, pb->state().lahead, + pb->state().lahead_end}; + + std::vector type_args; + + if ( meta.field() ) + type_args = meta.field()->arguments(); + + Expression default_ = + builder::default_(builder::typeByID(*unit->unitType().typeID()), std::move(type_args)); + + Expression self; + + if ( destination() ) { + self = *destination(); + builder()->addAssign(self, std::move(default_)); + } + else + self = builder()->addTmp("unit", std::move(default_)); + + auto call = builder::memberCall(self, "__parse_stage1", std::move(args)); + builder()->addAssign(builder::tuple({pb->state().cur, pb->state().lahead, pb->state().lahead_end}), call); + } + else { + const auto is_transient = (meta.field() && meta.field()->isTransient()); + + // We wrap the parsing of a non-atomic production into a new + // function that's cached and reused. This ensures correct + // operation for productions that recurse. + auto id = parse_functions.getOrCreate( + p.symbol(), [&]() { return unit ? ID("__parse_stage1") : ID(fmt("__parse_%s_stage1", p.symbol())); }, + [&](auto& id) { + auto id_stage1 = id; + auto id_stage2 = ID(fmt("__parse_%s_stage2", p.symbol())); + + std::optional addl_param; + + if ( destination() && ! unit && ! is_transient ) + addl_param = + builder::parameter("__dst", destination()->type(), declaration::parameter::Kind::InOut); + + // In the following, we structure the parsing into two + // stages. Depending on whether the unit may have + // filtered input, we either put these stages into + // separate functions where the 1st calls the 2nd (w/ + // filter support); or into just a single joint function + // doing both (w/o filtering). + + // Helper to wrap future code into a "try" block to catch + // errors, if necessary. + auto begin_try = [&](bool insert_try = true) -> std::optional { + if ( ! (unit && insert_try) ) + return {}; + + auto x = builder()->addTry(); + pushBuilder(x.first); + return x.second; + }; + + // Helper to close previous "try" block and report + // errors, if necessary. + auto end_try = [&](std::optional& try_) { + if ( ! try_ ) + return; + + popBuilder(); + + // TODO(robin) [copied here from the prototype]: + // Unclear if we should catch just ParseErrors + // here, or any exception. For now we catch them + // all, as that allows %error to trigger Bro + // events that would be missing otherwise. + auto catch_ = + try_->addCatch(builder::parameter(ID("e"), builder::typeByID("hilti::Exception"))); + pushBuilder(catch_, [&]() { + pb->finalizeUnit(false, p.location()); + builder()->addRethrow(); + }); + }; + + // First stage parse functionality implementing + // initialization and potentially filtering. + auto build_parse_stage1_logic = [&]() { + if ( unit ) { + auto field = p.meta().field(); + auto type = p.type(); + + std::string msg; + + if ( field && field->id() ) + msg = field->id(); + + if ( type && type->typeID() ) { + if ( msg.empty() ) + msg = *type->typeID(); + else + msg = fmt("%s: %s", msg, *type->typeID()); + } + + builder()->addDebugMsg("spicy", msg); + builder()->addCall("hilti::debugIndent", {builder::string("spicy")}); + } + + if ( unit ) + pb->initializeUnit(p.location()); + }; + + auto build_parse_stage1 = [&]() { + pushBuilder(); + + auto pstate = state(); + pstate.self = hilti::expression::UnresolvedID(ID("self")); + pstate.data = builder::id("__data"); + pstate.cur = builder::id("__cur"); + pstate.trim = builder::id("__trim"); + pstate.lahead = builder::id("__lah"); + pstate.lahead_end = builder::id("__lahe"); + + auto try_ = begin_try(); + + if ( unit ) + pstate.unit = unit->unitType(); + + pushState(std::move(pstate)); + + build_parse_stage1_logic(); + + // Call stage 2. + std::vector args = {state().data, state().cur, state().trim, state().lahead, + state().lahead_end}; + + if ( addl_param ) + args.push_back(builder::id(addl_param->id())); + + if ( unit && unit->unitType().supportsFilters() ) { + // If we have a filter attached, we initialize it and change to parse from its output. + auto filtered = + builder::local("filtered", builder::call("spicy_rt::filter_init", + {state().self, state().data, state().cur})); + + auto have_filter = builder()->addIf(filtered); + auto args2 = args; + have_filter->addLocal("filtered_data", type::ValueReference(type::Stream()), + builder::id("filtered")); + args2[0] = builder::id("filtered_data"); + args2[1] = builder::deref(args2[0]); + have_filter->addReturn(builder::memberCall(state().self, id_stage2, std::move(args2))); + } + + builder()->addReturn(builder::memberCall(state().self, id_stage2, std::move(args))); + + end_try(try_); + popState(); + + return popBuilder()->block(); + }; // End of build_parse_stage1() + + // Second stage parse functionality implementing the main + // part of the unit's parsing. + auto build_parse_stage2_logic = [&]() { + if ( destination() && ! unit && ! is_transient ) + pushDestination(builder::type_wrapped(builder::id("__dst"), destination()->type())); + + if ( unit || is_transient ) + pushDestination({}); + + if ( auto x = dispatch(p); ! x ) + hilti::logger().internalError( + fmt("ParserBuilder: non-atomic production %s not handled (%s)", p.typename_(), p)); + + if ( unit ) + builder()->addCall("hilti::debugDedent", {builder::string("spicy")}); + + auto result = builder::tuple({ + state().cur, + state().lahead, + state().lahead_end, + }); + + return result; + }; + + auto build_parse_stage12_or_stage2 = [&](bool join_stages) { + auto pstate = state(); + pstate.self = hilti::expression::UnresolvedID(ID("self")); + pstate.data = builder::id("__data"); + pstate.cur = builder::id("__cur"); + pstate.trim = builder::id("__trim"); + pstate.lahead = builder::id("__lah"); + pstate.lahead_end = builder::id("__lahe"); + + if ( unit ) + pstate.unit = unit->unitType(); + + pushState(std::move(pstate)); + pushBuilder(); + + auto try_ = begin_try(join_stages); + + if ( join_stages ) + build_parse_stage1_logic(); + + auto result = build_parse_stage2_logic(); + builder()->addReturn(std::move(result)); + + end_try(try_); + popState(); + + if ( unit || destination() ) + popDestination(); + + return popBuilder()->block(); + }; // End of build_parse_stage2() + + // Add the parse methods. Note the unit's primary + // stage1 method is already declared (but not + // implemented) by the struct that unit-builder is + // declaring. + if ( unit->unitType().supportsFilters() ) { + addParseMethod(id_stage1.str() != "__parse_stage1", id_stage1, build_parse_stage1(), addl_param, + p.location()); + addParseMethod(true, id_stage2, build_parse_stage12_or_stage2(false), addl_param, p.location()); + } + else + addParseMethod(id_stage1.str() != "__parse_stage1", id_stage1, + build_parse_stage12_or_stage2(true), addl_param, p.location()); + + return id_stage1; + }); + + std::vector args = { + state().data, state().cur, state().trim, state().lahead, state().lahead_end, + }; + + auto dst = destination(); + + if ( unit ) + dst = {}; + + if ( dst && ! is_transient ) + args.push_back(*dst); + + auto call = builder::memberCall(state().self, id, args); + builder()->addAssign(builder::tuple({state().cur, state().lahead, state().lahead_end}), call); + } + + Expression stop = builder::bool_(false); + + if ( container_element ) { + popDestination(); + stop = pb->newContainerItem(*meta.container(), *destination(), *container_element); + } + + endProduction(p); + + if ( is_field_owner ) + postParseField(p, *meta.field()); + + return stop; + } + + void preParseField(const Production& /* i */, const production::Meta& meta) { + // Helper function that returns a computed type that delays + // determining a field's actual type to when it's needed. + auto field_type = [&]() -> Type { + auto callback = [](Node& n) { return n.as().itemType(); }; + + assert(meta.fieldRef()); + return type::Computed(meta.fieldRef(), callback); + }; + + pb->enableDefaultNewValueForField(true); + + auto field = *meta.field(); + + if ( field.parseType().isA() ) { + // No value to store. + pushDestination(hilti::expression::Void()); + } + else if ( AttributeSet::find(field.attributes(), "&convert") ) { + // Need a temporary for the parsed field. + auto dst = builder()->addTmp(fmt("parsed_%s", field.id()), field.parseType()); + pushDestination(dst); + } + else if ( field.isTransient() ) { + // Won't have the field in the emitted C++ code, so we need a temporary. + auto ftype = field_type(); + auto dst = builder()->addTmp(fmt("transient_%s", field.id()), ftype); + pushDestination(builder::type_wrapped(dst, ftype, field.meta())); + } + else { + // Can store parsed value directly in struct field. + auto dst = builder::member(pb->state().self, field.id()); + pushDestination(builder::type_wrapped(dst, field_type(), field.meta())); + } + + if ( auto a = AttributeSet::find(field.attributes(), "&parse-from") ) { + // Redirect input to a bytes value. + auto pstate = state(); + pstate.trim = builder::bool_(false); + pstate.lahead = builder()->addTmp("parse_lah", look_ahead::Type, look_ahead::None); + pstate.lahead_end = builder()->addTmp("parse_lahe", type::stream::Iterator()); + auto expr = a->valueAs(); + + auto tmp = builder()->addTmp("parse_from", type::ValueReference(type::Stream()), *expr); + pstate.data = tmp; + pstate.cur = builder()->addTmp("parse_cur", type::stream::View(), builder::deref(tmp)); + builder()->addMemberCall(tmp, "freeze", {}); + + pushState(std::move(pstate)); + } + + if ( auto a = AttributeSet::find(field.attributes(), "&parse-at") ) { + // Redirect input to a stream position. + auto pstate = state(); + pstate.trim = builder::bool_(false); + pstate.lahead = builder()->addTmp("parse_lah", look_ahead::Type, look_ahead::None); + pstate.lahead_end = builder()->addTmp("parse_lahe", type::stream::Iterator()); + auto expr = a->valueAs(); + + auto cur = builder::memberCall(state().cur, "advance", {*expr}); + + pstate.cur = builder()->addTmp("parse_cur", cur); + pushState(std::move(pstate)); + } + + if ( auto c = field.condition() ) + pushBuilder(builder()->addIf(*c)); + + if ( auto a = AttributeSet::find(field.attributes(), "&size") ) { + // Limit input to the specified length. + auto length = builder::coerceTo(*a->valueAs(), type::UnsignedInteger(64)); + auto limited = builder()->addTmp("limited", builder::memberCall(state().cur, "limit", {length})); + + // Establish limited view, remembering position to continue at. + // We always advance by the full amount eventually (as saved + // here), even though generally the parsing might not consume + // everything. That way, &size can be used to unconditionally + // skip a certain of amount of data. + auto pstate = state(); + pstate.cur = limited; + pstate.ncur = builder()->addTmp("ncur", builder::memberCall(state().cur, "advance", {length})); + pushState(std::move(pstate)); + } + } + + void postParseField(const Production& /* i */, const type::unit::item::Field& field) { + auto ncur = state().ncur; + state().ncur = {}; + + if ( auto a = AttributeSet::find(field.attributes(), "&size") ) + popState(); + + if ( AttributeSet::find(field.attributes(), "&parse-from") || + AttributeSet::find(field.attributes(), "&parse-at") ) { + ncur = {}; + popState(); + } + + auto dst = popDestination(); + + if ( dst && pb->isEnabledDefaultNewValueForField() && state().literal_mode == LiteralMode::Default ) + pb->newValueForField(field, *dst); + + if ( ncur ) + builder()->addAssign(state().cur, *ncur); + + if ( field.condition() ) + popBuilder(); + } + + // Returns a boolean expression that's 'true' if a 'stop' was encountered. + Expression parseProduction(const Production& p) { return _parseProduction(p, p.meta(), false); } + + // Returns a boolean expression that's 'true' if a 'stop' was encountered. + Expression parseProduction(const Production& p, const Production& forwarded_to) { + return _parseProduction(forwarded_to, p.meta(), true); + } + + // Retrieve a look-ahead symbol. Once the code generated by the function + // has executed, the parsing state will reflect what look-ahead has been + // found, including `EOD` if `cur` is the end-of-data, and `None` if no + // expected look-ahead token is found. + void getLookAhead(const production::LookAhead& lp) { + // If we're at EOD, return that directly. + auto [true_, false_] = builder()->addIfElse(pb->atEod()); + true_->addAssign(state().lahead, look_ahead::Eod); + + pushBuilder(false_); + + // Collect all expected terminals. + auto& lahs = lp.lookAheads(); + auto tokens = util::set_union(lahs.first, lahs.second); + + auto regexps = std::vector(); + auto other = std::vector(); + std::partition_copy(tokens.begin(), tokens.end(), std::back_inserter(regexps), std::back_inserter(other), + [](auto& p) { return p.type()->template isA(); }); + + bool first_token = true; + + // Parse regexps in parallel. + if ( ! regexps.empty() ) { + first_token = false; + + // Create the joint regular expression. The token IDs become the regexps' IDs. + auto patterns = util::transform(regexps, [](const auto& c) { + return std::make_pair(c.template as() + .ctor() + .template as() + .value(), + c.tokenID()); + }); + + auto flattened = std::vector(); + + for ( const auto& p : patterns ) { + for ( const auto& r : p.first ) + flattened.push_back(util::fmt("%s{#%" PRId64 "}", r, p.second)); + } + + auto re = hilti::ID(fmt("__re_%" PRId64, lp.symbol())); + auto d = builder::constant(re, builder::regexp(flattened, AttributeSet({Attribute("&nosub")}))); + pb->cg()->addDeclaration(d); + + // Create the token matcher state. + builder()->addLocal(ID("ncur"), state().cur); + auto ms = builder::local("ms", builder::memberCall(builder::id(re), "token_matcher", {})); + + // Create the loop around the incremental matching. + auto body = builder()->addWhile(ms, builder::bool_(true)); + pushBuilder(body); + + builder()->addLocal(ID("rc"), hilti::type::SignedInteger(32)); + + builder()->addAssign(builder::tuple({builder::id("rc"), builder::id("ncur")}), + builder::memberCall(builder::id("ms"), "advance", {builder::id("ncur")}), + lp.location()); + + auto switch_ = builder()->addSwitch(builder::id("rc"), lp.location()); + + auto no_match_try_again = switch_.addCase(builder::integer(-1)); + pushBuilder(no_match_try_again); + auto ok = builder()->addIf(pb->waitForInputOrEod()); + ok->addContinue(); + builder()->addAssign(state().lahead, look_ahead::Eod); + builder()->addAssign(state().lahead_end, builder::begin(state().cur)); + builder()->addBreak(); + popBuilder(); + + auto no_match_error = switch_.addCase(builder::integer(0)); + pushBuilder(no_match_error); + builder()->addAssign(state().lahead, look_ahead::None); + builder()->addAssign(state().lahead_end, builder::begin(state().cur)); + builder()->addBreak(); + popBuilder(); + + auto match = switch_.addDefault(); + pushBuilder(match); + builder()->addAssign(state().lahead, builder::id("rc")); + builder()->addAssign(state().lahead_end, builder::begin(builder::id("ncur"))); + builder()->addBreak(); + popBuilder(); + + popBuilder(); // End of switch body + } + + // Parse non-regexps successively. + for ( auto& p : other ) { + if ( ! p.isLiteral() ) + continue; + + auto pstate = pb->state(); + pstate.literal_mode = LiteralMode::Try; + pushState(std::move(pstate)); + auto match = pb->parseLiteral(p, {}); + popState(); + + if ( first_token ) { + // Simplified version, no previous match possible that we + // would need to compare against. + first_token = false; + auto true_ = builder()->addIf(builder::unequal(match, builder::begin(state().cur))); + true_->addAssign(state().lahead, builder::integer(p.tokenID())); + true_->addAssign(state().lahead_end, match); + } + else { + // If the length is larger than any token we have found so + // far, we take it. If length is the same as previous one, + // it's ambiguous and we bail out. + auto true_ = + builder()->addIf(builder::local("i", match), + builder::and_(builder::unequal(builder::id("i"), builder::begin(state().cur)), + builder::greaterEqual(builder::id("i"), state().lahead_end))); + + auto ambiguous = true_->addIf(builder::and_(builder::unequal(state().lahead, look_ahead::None), + builder::equal(builder::id("i"), state().lahead_end))); + pushBuilder(ambiguous); + pb->parseError("ambiguous look-ahead token match", lp.location()); + popBuilder(); + + true_->addAssign(state().lahead, builder::integer(p.tokenID())); + true_->addAssign(state().lahead_end, builder::id("i")); + } + } + + popBuilder(); + } + + // Adds a method, and its implementation, to the current parsing struct + // type that has the standard signature for parse methods. + void addParseMethod(bool add_decl, const ID& id, Statement body, + std::optional addl_param = {}, const Meta& m = {}) { + auto qualified_id = pb->state().unit_id + id; + + auto ftype = pb->parseMethodFunctionType(std::move(addl_param), m); + auto func = builder::function(qualified_id, ftype, std::move(body), declaration::Linkage::Struct, + function::CallingConvention::Standard, {}, m); + + if ( add_decl ) + new_fields.emplace_back(type::struct_::Field(id, func.function().type())); + + cg()->addDeclaration(func); + } + + void operator()(const production::Epsilon& /* p */) {} + + void operator()(const production::Counter& p) { + auto body = builder()->addWhile(builder::local("__i", hilti::type::UnsignedInteger(64), p.expression()), + builder::id("__i")); + + pushBuilder(body); + body->addExpression(builder::decrementPostfix(builder::id("__i"))); + + auto stop = parseProduction(p.body()); + auto b = builder()->addIf(stop); + b->addBreak(); + popBuilder(); + } + + void operator()(const production::Enclosure& p) { + builder()->addCall("hilti::debugIndent", {builder::string("spicy")}); + parseProduction(p.child()); + builder()->addCall("hilti::debugDedent", {builder::string("spicy")}); + } + + void operator()(const production::ForEach& p) { + Expression cond; + + if ( p.eodOk() ) + cond = builder::not_(builder::call("spicy_rt::atEod", {state().data, state().cur})); + else + cond = builder::bool_(true); + + auto body = builder()->addWhile(cond); + pushBuilder(body); + auto stop = parseProduction(p.body()); + auto b = builder()->addIf(stop); + b->addBreak(); + popBuilder(); + } + + void operator()(const production::Resolved& p) { parseProduction(p, grammar.resolved(p)); } + + void operator()(const production::Switch& p) { + builder()->addCall("hilti::debugIndent", {builder::string("spicy")}); + + auto switch_ = builder()->addSwitch(p.expression(), p.location()); + + for ( const auto& [exprs, prod] : p.cases() ) { + auto case_ = switch_.addCase(exprs, prod.location()); + pushBuilder(case_, [&, prod = std::ref(prod)]() { parseProduction(prod); }); + } + + if ( auto prod = p.default_() ) { + auto default_ = switch_.addDefault(prod->location()); + pushBuilder(default_, [&]() { parseProduction(*prod); }); + } + else { + auto default_ = switch_.addDefault(p.location()); + pushBuilder(default_, [&]() { pb->parseError("no matching case in switch statement", p.location()); }); + } + + builder()->addCall("hilti::debugDedent", {builder::string("spicy")}); + } + + void operator()(const production::Unit& p) { + assert(! destination()); // parseProduction() ensures this, destination is "this". + + auto pstate = pb->state(); + pstate.self = builder::id("self"); + pushState(std::move(pstate)); + + if ( p.unitType().usesRandomAccess() ) { + // Disable trimming. + auto pstate = state(); + pstate.trim = builder::bool_(false); + pushState(std::move(pstate)); + } + + for ( const auto& i : p.fields() ) + parseProduction(i); + + pb->finalizeUnit(true, p.location()); + popState(); + + if ( p.unitType().usesRandomAccess() ) + popState(); + } + + void operator()(const production::Ctor& p) { pb->parseLiteral(p, destination()); } + + void operator()(const production::LookAhead& p) { + assert(state().needs_look_ahead); + + // If we don't have a look-ahead symbol pending, get one. + auto true_ = builder()->addIf(builder::not_(state().lahead)); + pushBuilder(true_); + getLookAhead(p); + popBuilder(); + + // Now use the freshly set look-ahead symbol to switch accordingly. + auto& lahs = p.lookAheads(); + + auto alts1 = util::filter(lahs.first, [](const auto& p) { return p.isLiteral(); }); + auto alts2 = util::filter(lahs.second, [](const auto& p) { return p.isLiteral(); }); + auto exprs_alt1 = util::transform_to_vector(alts1, [](const auto& p) { return builder::integer(p.tokenID()); }); + auto exprs_alt2 = util::transform_to_vector(alts2, [](const auto& p) { return builder::integer(p.tokenID()); }); + + switch ( p.default_() ) { + case production::look_ahead::Default::First: { + exprs_alt1.push_back(look_ahead::None); + break; + } + case production::look_ahead::Default::Second: { + exprs_alt2.push_back(look_ahead::None); + break; + } + case production::look_ahead::Default::None: { + if ( alts1.empty() ) + exprs_alt1.push_back(look_ahead::None); + + if ( alts2.empty() ) + exprs_alt2.push_back(look_ahead::None); + + break; + } + } + + // If one alternative has no look-aheads and is just epsilon, then + // EOD is OK and we go there if we haven't found a look-ahead symbol. + bool eod_handled = true; + + if ( lahs.first.empty() && p.alternatives().first.isA() ) + exprs_alt1.push_back(look_ahead::Eod); + else if ( lahs.second.empty() && p.alternatives().second.isA() ) + exprs_alt2.push_back(look_ahead::Eod); + else + eod_handled = false; + + auto switch_ = builder()->addSwitch(state().lahead); + auto builder_alt1 = switch_.addCase(std::move(exprs_alt1)); + pushBuilder(builder_alt1); + parseProduction(p.alternatives().first); + popBuilder(); + + auto builder_alt2 = switch_.addCase(std::move(exprs_alt2)); + pushBuilder(builder_alt2); + parseProduction(p.alternatives().second); + popBuilder(); + + if ( ! eod_handled ) { + auto builder_eod = switch_.addCase(look_ahead::Eod); + pushBuilder(builder_eod); + pb->parseError("expected look-ahead token, but reached end-of-data", p.location()); + popBuilder(); + } + + auto builder_default = switch_.addDefault(); + pushBuilder(builder_default); + pb->parseError("no expected look-ahead token found", p.location()); + popBuilder(); + } + + void operator()(const production::Sequence& p) { + for ( const auto& i : p.sequence() ) + parseProduction(i); + } + + void operator()(const production::Variable& p) { + auto field = p.meta().field(); + pb->parseType(p.type(), field, destination()); + } +}; + +} // namespace spicy::detail::codegen + +static auto parseMethodIDs(const type::Unit& t) { + assert(t.typeID()); + return std::make_tuple(ID(fmt("%s::parse1", *t.typeID())), ID(fmt("%s::parse2", *t.typeID()))); +} + +hilti::type::Function ParserBuilder::parseMethodFunctionType(std::optional addl_param, + const Meta& m) { + auto result = type::Tuple({type::stream::View(), look_ahead::Type, type::stream::Iterator()}); + + auto params = std::vector{ + builder::parameter("__data", type::ValueReference(type::Stream()), declaration::parameter::Kind::InOut), + builder::parameter("__cur", type::stream::View(), declaration::parameter::Kind::Copy), + builder::parameter("__trim", type::Bool(), declaration::parameter::Kind::Copy), + builder::parameter("__lah", look_ahead::Type, declaration::parameter::Kind::Copy), + builder::parameter("__lahe", type::stream::Iterator(), declaration::parameter::Kind::Copy), + }; + + if ( addl_param ) + params.push_back(*addl_param); + + return type::Function(type::function::Result(std::move(result), m), params, hilti::type::function::Flavor::Method, + m); +} + +const std::shared_ptr& ParserBuilder::context() const { return _cg->context(); } + +const hilti::Options& ParserBuilder::options() const { return _cg->options(); } + +std::shared_ptr ParserBuilder::pushBuilder() { + _builders.emplace_back(std::make_shared(context())); + return _builders.back(); +} + +hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const type::Unit& t, bool declare_only) { + auto [id_ext_overload1, id_ext_overload2] = parseMethodIDs(t); + + std::vector params = {builder::parameter("data", type::ValueReference(type::Stream()), + declaration::parameter::Kind::InOut), + builder::parameter("cur", type::Optional(type::stream::View()), + builder::optional(type::stream::View()))}; + + + for ( auto p : t.parameters() ) + params.emplace_back(p); + + auto f_ext_overload1_result = type::stream::View(); + auto f_ext_overload1 = + builder::function(id_ext_overload1, f_ext_overload1_result, std::move(params), type::function::Flavor::Method, + declaration::Linkage::Struct, function::CallingConvention::Extern, + AttributeSet({Attribute("&static")}), t.meta()); + + auto f_ext_overload2_result = type::stream::View(); + auto f_ext_overload2 = + builder::function(id_ext_overload2, f_ext_overload2_result, + {builder::parameter("unit", hilti::type::UnresolvedID(*t.typeID()), + declaration::parameter::Kind::InOut), + builder::parameter("data", type::ValueReference(type::Stream()), + declaration::parameter::Kind::InOut), + builder::parameter("cur", type::Optional(type::stream::View()), + builder::optional(type::stream::View()))}, + type::function::Flavor::Method, declaration::Linkage::Struct, + function::CallingConvention::Extern, AttributeSet({Attribute("&static")}), t.meta()); + + // We only actually add the functions we just build if the unit is + // publically exposed. We still build their code in either case below + // because doing so triggers generation of the whole parser, including + // the internal parsing functions. + auto sf_ext_overload1 = + type::struct_::Field(f_ext_overload1.id().local(), function::CallingConvention::Extern, + f_ext_overload1.function().type(), f_ext_overload1.function().attributes()); + auto sf_ext_overload2 = + type::struct_::Field(f_ext_overload2.id().local(), function::CallingConvention::Extern, + f_ext_overload2.function().type(), f_ext_overload2.function().attributes()); + + s = hilti::type::Struct::addField(s, sf_ext_overload1); + s = hilti::type::Struct::addField(s, sf_ext_overload2); + + if ( ! declare_only ) { + auto grammar = cg()->grammarBuilder()->grammar(t); + auto visitor = ProductionVisitor(this, grammar); + + if ( t.parameters().empty() ) { + // Create parse1() body. + pushBuilder(); + builder()->addLocal("unit", + builder::value_reference(builder::default_(builder::typeByID(*t.typeID()), + util::transform(t.parameters(), [](auto p) { + return builder::id(p.id()); + })))); + builder()->addLocal("ncur", type::stream::View(), + builder::ternary(builder::id("cur"), builder::deref(builder::id("cur")), + builder::cast(builder::deref(builder::id("data")), + type::stream::View()))); + builder()->addLocal("lahead", look_ahead::Type, look_ahead::None); + builder()->addLocal("lahead_end", type::stream::Iterator()); + + auto pstate = ParserState(t, grammar, builder::id("data"), builder::id("cur")); + pstate.self = builder::id("unit"); + pstate.cur = builder::id("ncur"); + pstate.trim = builder::bool_(true); + pstate.lahead = builder::id("lahead"); + pstate.lahead_end = builder::id("lahead_end"); + pushState(pstate); + visitor.pushDestination(builder::id("unit")); + visitor.parseProduction(*grammar.root()); + visitor.popDestination(); + builder()->addReturn(builder::move(state().cur)); + popState(); + + auto body_ext_overload1 = popBuilder(); + auto d_ext_overload1 = hilti::declaration::Function::setBody(f_ext_overload1, body_ext_overload1->block()); + cg()->addDeclaration(d_ext_overload1); + } + + // Create parse2() body. + pushBuilder(); + builder()->addLocal("ncur", type::stream::View(), + builder::ternary(builder::id("cur"), builder::deref(builder::id("cur")), + builder::cast(builder::deref(builder::id("data")), type::stream::View()))); + builder()->addLocal("lahead", look_ahead::Type, look_ahead::None); + builder()->addLocal("lahead_end", type::stream::Iterator()); + + auto pstate = ParserState(t, grammar, builder::id("data"), builder::id("cur")); + pstate.self = builder::id("unit"); + pstate.cur = builder::id("ncur"); + pstate.trim = builder::bool_(true); + pstate.lahead = builder::id("lahead"); + pstate.lahead_end = builder::id("lahead_end"); + pushState(pstate); + visitor.pushDestination(builder::id("unit")); + visitor.parseProduction(*grammar.root()); + visitor.popDestination(); + builder()->addReturn(builder::move(state().cur)); + popState(); + + auto body_ext_overload2 = popBuilder(); + + auto d_ext_overload2 = hilti::declaration::Function::setBody(f_ext_overload2, body_ext_overload2->block()); + cg()->addDeclaration(d_ext_overload2); + + for ( auto f : visitor.new_fields ) + s = hilti::type::Struct::addField(s, std::move(f)); + } + + return s; +} + +Expression ParserBuilder::parseMethodExternalOverload1(const type::Unit& t) { + auto id = std::get<0>(parseMethodIDs(t)); + return hilti::expression::UnresolvedID(std::move(id)); +} + +Expression ParserBuilder::parseMethodExternalOverload2(const type::Unit& t) { + auto id = std::get<1>(parseMethodIDs(t)); + return hilti::expression::UnresolvedID(std::move(id)); +} + +void ParserBuilder::newValueForField(const type::unit::item::Field& field, const Expression& value) { + if ( value.type().isA() ) { + // Special-case: No value parsed, but still run hook. + beforeHook(); + builder()->addMemberCall(state().self, ID(fmt("__on_%s", field.id().local())), {}, field.meta()); + afterHook(); + return; + } + + auto nvalue = value; + + if ( auto a = AttributeSet::find(field.attributes(), "&convert"); a && ! field.isTransient() ) { + // Value was stored in temporary. Apply expression and store result + // at destination. + auto block = builder()->addBlock(); + block->addLocal(ID("__dd"), field.parseType(), value); + block->addAssign(builder::member(state().self, field.id()), *a->valueAs()); + nvalue = builder::member(state().self, field.id()); + } + + if ( ! field.parseType().isA() ) { + builder()->addDebugMsg("spicy", fmt("%s = %%s", field.id()), {nvalue}); + builder()->addDebugMsg("spicy-verbose", fmt("- setting field '%s' to '%%s'", field.id()), {nvalue}); + } + + for ( const auto& s : field.sinks() ) { + builder()->addDebugMsg("spicy-verbose", "- writing %" PRIu64 " bytes to sink", {builder::size(nvalue)}); + builder()->addMemberCall(builder::deref(s), "write", {nvalue, builder::null(), builder::null()}, field.meta()); + } + + beforeHook(); + builder()->addMemberCall(state().self, ID(fmt("__on_%s", field.id().local())), {nvalue}, field.meta()); + afterHook(); +} + +Expression ParserBuilder::newContainerItem(const type::unit::item::Field& field, const Expression& self, + const Expression& item) { + auto stop = builder()->addTmp("stop", builder::bool_(false)); + std::optional addl_stop_condition; + + bool push_after_condition = true; + + if ( auto a = AttributeSet::find(field.attributes(), "&until") ) + addl_stop_condition = a->valueAs(); + + if ( auto a = AttributeSet::find(field.attributes(), "&until_including") ) { + addl_stop_condition = a->valueAs(); + push_after_condition = false; + } + + if ( auto a = AttributeSet::find(field.attributes(), "&while") ) + addl_stop_condition = builder::not_(*a->valueAs()); + + // The following makes sure that (1) both hook and vector will see the + // same fields; (2) at the time the hook runs, the element has not yet + // been added to the vector; and (3) the hook can signal "stop" as well, + // overriding the condition. + + auto run_hook = [&]() { + builder()->addDebugMsg("spicy-verbose", "- got container item"); + beforeHook(); + builder()->addMemberCall(state().self, ID(fmt("__on_%s_foreach", field.id().local())), {item, stop}, + field.meta()); + afterHook(); + }; + + auto eval_condition = [&]() { + if ( ! addl_stop_condition ) + return; + + pushBuilder(builder()->addBlock(), [&]() { + builder()->addLocal("__dd", item); + builder()->addAssign(stop, builder::or_(stop, *addl_stop_condition)); + }); + }; + + if ( push_after_condition ) { + eval_condition(); + + pushBuilder(builder()->addIf(builder::not_(stop)), [&]() { run_hook(); }); + + if ( ! field.isTransient() ) + pushBuilder(builder()->addIf(builder::not_(stop)), [&]() { + builder()->addExpression(builder::memberCall(self, "push_back", {builder::move(item)})); + }); + } + else { + run_hook(); + + if ( ! field.isTransient() ) + pushBuilder(builder()->addIf(builder::not_(stop)), [&]() { + builder()->addExpression(builder::memberCall(self, "push_back", {builder::move(item)})); + }); + + eval_condition(); + } + + return stop; +} + +void ParserBuilder::trimInput(bool force) { + auto do_trim = [this](const auto& builder) { + builder->addDebugMsg("spicy-verbose", "- trimming input"); + builder->addExpression(builder::memberCall(state().data, "trim", {builder::begin(state().cur)})); + }; + + if ( force ) + do_trim(builder()); + else + do_trim(builder()->addIf(state().trim)); +} + +void ParserBuilder::initializeUnit(const Location& l) { + const auto& unit = state().unit.get(); + + if ( unit.usesRandomAccess() ) { + // Save the current input offset for the raw access methods. + builder()->addAssign(builder::member(state().self, ID("__begin")), builder::begin(state().cur)); + builder()->addAssign(builder::member(state().self, ID("__position")), builder::begin(state().cur)); + } + + beforeHook(); + builder()->addMemberCall(state().self, "__on_0x25_init", {}, l); + afterHook(); +} + +void ParserBuilder::finalizeUnit(bool success, const Location& l) { + const auto& unit = state().unit.get(); + + if ( success ) { + beforeHook(); + builder()->addMemberCall(state().self, "__on_0x25_done", {}, l); + afterHook(); + } + else + builder()->addMemberCall(state().self, "__on_0x25_error", {}, l); + + if ( unit.supportsFilters() ) + builder()->addCall("spicy_rt::filter_disconnect", {state().self}); + + for ( const auto& s : unit.items() ) + builder()->addMemberCall(builder::member(state().self, s.id()), "close", {}, l); +} + +static Expression _filters(const ParserState& state) { + hilti::Expression filters; + + if ( state.unit.get().supportsFilters() ) + return builder::member(state.self, ID("__filters")); + + return builder::null(); +} + +Expression ParserBuilder::waitForInputOrEod() { + return builder::call("spicy_rt::waitForInputOrEod", {state().data, state().cur, _filters(state())}); +} + +Expression ParserBuilder::atEod() { return builder::call("spicy_rt::atEod", {state().data, state().cur}); } + +void ParserBuilder::waitForInput(const std::string& error_msg, const Meta& location) { + builder()->addCall("spicy_rt::waitForInput", {state().data, state().cur, builder::string(error_msg), + builder::expression(location), _filters(state())}); +} + +Expression ParserBuilder::waitForInputOrEod(const Expression& min) { + return builder::call("spicy_rt::waitForInputOrEod", {state().data, state().cur, min, _filters(state())}); +} + +void ParserBuilder::waitForInput(const Expression& min, const std::string& error_msg, const Meta& location) { + builder()->addCall("spicy_rt::waitForInput", {state().data, state().cur, min, builder::string(error_msg), + builder::expression(location), _filters(state())}); +} + +void ParserBuilder::waitForEod() { + builder()->addCall("spicy_rt::waitForEod", {state().data, state().cur, _filters(state())}); +} + +void ParserBuilder::parseError(const Expression& error_msg, const Meta& location) { + builder()->addThrow(builder::exception(builder::typeByID("spicy_rt::ParseError"), error_msg, location), location); +} + +void ParserBuilder::parseError(const std::string& error_msg, const Meta& location) { + parseError(builder::string(error_msg), location); +} + +void ParserBuilder::advanceInput(const Expression& i) { + if ( i.type().isA() ) + builder()->addAssign(state().cur, i); + else + builder()->addAssign(state().cur, builder::memberCall(state().cur, "advance", {i})); + + trimInput(); +} + +void ParserBuilder::setInput(const Expression& i) { builder()->addAssign(state().cur, i); } + +void ParserBuilder::beforeHook() { + if ( state().unit.get().usesRandomAccess() ) + builder()->addAssign(builder::member(state().self, ID("__position_update")), + builder::optional(hilti::type::stream::Iterator())); +} + +void ParserBuilder::afterHook() { + if ( state().unit.get().usesRandomAccess() ) { + auto position_update = builder::member(state().self, ID("__position_update")); + auto advance = builder()->addIf(position_update); + auto ncur = builder::memberCall(state().cur, "advance", {builder::deref(position_update)}); + + if ( state().ncur ) + advance->addAssign(*state().ncur, ncur); + else + advance->addAssign(state().cur, ncur); + + advance->addAssign(builder::member(state().self, ID("__position_update")), + builder::optional(hilti::type::stream::Iterator())); + } +} + +void ParserBuilder::saveParsePosition() { + if ( ! state().unit.get().usesRandomAccess() ) + return; + + builder()->addAssign(builder::member(state().self, ID("__position")), builder::begin(state().cur)); +} + +void ParserBuilder::consumeLookAhead(std::optional dst) { + builder()->addDebugMsg("spicy-verbose", "- consuming look-ahead token"); + + if ( dst ) + builder()->addAssign(*dst, builder::memberCall(state().cur, "sub", {state().lahead_end})); + + builder()->addAssign(state().lahead, look_ahead::None); + advanceInput(state().lahead_end); +} diff --git a/spicy/src/compiler/codegen/parsers/literals.cc b/spicy/src/compiler/codegen/parsers/literals.cc new file mode 100644 index 000000000..e7caa8479 --- /dev/null +++ b/spicy/src/compiler/codegen/parsers/literals.cc @@ -0,0 +1,223 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +#include + +using namespace spicy; +using namespace spicy::detail; +using namespace spicy::detail::codegen; +using util::fmt; + +namespace builder = hilti::builder; + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + Visitor(ParserBuilder* pb, const Production& p, const std::optional& dst) + : pb(pb), production(p), dst(dst) {} + ParserBuilder* pb; + const Production& production; + const std::optional& dst; + + auto state() { return pb->state(); } + auto builder() { return pb->builder(); } + auto pushBuilder(std::shared_ptr b) { return pb->pushBuilder(std::move(b)); } + auto pushBuilder() { return pb->pushBuilder(); } + auto popBuilder() { return pb->popBuilder(); } + + Expression destination(const Type& t) { + if ( dst ) + return *dst; + + if ( auto field = production.meta().field() ) + return builder()->addTmp("c", field->parseType()); + + return builder()->addTmp("c", t); + } + + result_t operator()(const hilti::ctor::Bytes& c) { + auto error_msg = fmt("expecting '%s'", c.value()); + auto len = builder::integer(static_cast(c.value().size())); + auto cond = builder::memberCall(state().cur, "starts_with", {builder::expression(c)}); + + switch ( state().literal_mode ) { + case LiteralMode::Default: { + auto [have_lah, no_lah] = builder()->addIfElse(state().lahead); + + pushBuilder(have_lah); + builder()->addAssert(builder::equal(state().lahead, builder::integer(production.tokenID())), + "unexpected token to consume"); + builder()->addAssert(hilti::builder::equal(builder::expression(c), + builder::memberCall(state().cur, "sub", + {builder::begin(state().cur), + state().lahead_end})), + "unexpected data when consuming token"); + pb->consumeLookAhead(); + popBuilder(); + + pushBuilder(no_lah); + pb->waitForInput(len, error_msg, c.meta()); + auto no_match = builder()->addIf(builder::not_(cond)); + pushBuilder(no_match); + pb->parseError(error_msg, c.meta()); + popBuilder(); + + pb->advanceInput(len); + popBuilder(); + + builder()->addAssign(destination(c.type()), builder::expression(c)); + return builder::expression(c); + } + + case LiteralMode::Try: + return builder::ternary(builder::and_(pb->waitForInputOrEod(len), cond), + builder::sum(builder::begin(state().cur), len), builder::begin(state().cur)); + + default: util::cannot_be_reached(); + } + } + + result_t operator()(const hilti::ctor::RegExp& c) { + auto re = hilti::ID(fmt("__re_%" PRId64, production.tokenID())); + + if ( ! pb->cg()->haveAddedDeclaration(re) ) { + auto d = builder::constant(re, builder::regexp(c.value(), AttributeSet({Attribute("&nosub")}))); + pb->cg()->addDeclaration(d); + } + + switch ( state().literal_mode ) { + case LiteralMode::Default: { + auto [have_lah, no_lah] = builder()->addIfElse(state().lahead); + auto dst = destination(type::Bytes()); + + pushBuilder(have_lah); + builder()->addAssert(builder::equal(state().lahead, builder::integer(production.tokenID())), + "unexpected token to consume"); + pb->consumeLookAhead(dst); + popBuilder(); + + pushBuilder(no_lah); + + builder()->addLocal(ID("ncur"), state().cur); + auto ms = builder::local("ms", builder::memberCall(builder::id(re), "token_matcher", {})); + auto body = builder()->addWhile(ms, builder::bool_(true)); + pushBuilder(body); + + builder()->addLocal(ID("rc"), hilti::type::SignedInteger(32)); + + builder()->addAssign(builder::tuple({builder::id("rc"), builder::id("ncur")}), + builder::memberCall(builder::id("ms"), "advance", {builder::id("ncur")}), + c.meta()); + + auto switch_ = builder()->addSwitch(builder::id("rc"), c.meta()); + + auto no_match_try_again = switch_.addCase(builder::integer(-1)); + pushBuilder(no_match_try_again); + pb->waitForInput("end of data while matching regular expression", c.meta().location()); + no_match_try_again->addContinue(); + popBuilder(); + + auto no_match_error = switch_.addCase(builder::integer(0)); + pushBuilder(no_match_error); + pb->parseError("failed to match regular expression", c.meta()); + popBuilder(); + + auto match = switch_.addDefault(); + pushBuilder(match); + builder()->addAssign(dst, + builder::memberCall(state().cur, "sub", {builder::begin(builder::id("ncur"))})); + pb->setInput(builder::id("ncur")); + builder()->addBreak(); + popBuilder(); + + popBuilder(); + + popBuilder(); + + return dst; + } + + case LiteralMode::Try: + // RegExp are special-cased by the parser generator and handled + // directly there, so that we should get here in try mode. + hilti::logger().internalError("unexpected literal mode Try when parsing regexp literals"); + default: util::cannot_be_reached(); + } + } + + result_t operator()(const hilti::expression::Ctor& c) { return *dispatch(c.ctor()); } + + result_t parseInteger(const Type& type, const Expression& expected, const Meta& meta) { + auto offset = [](Expression view) { return builder::memberCall(view, "offset", {}); }; + + switch ( state().literal_mode ) { + case LiteralMode::Default: { + auto [have_lah, no_lah] = builder()->addIfElse(state().lahead); + + pushBuilder(have_lah); + builder()->addAssert(builder::equal(state().lahead, builder::integer(production.tokenID())), + "unexpected token to consume"); + pb->consumeLookAhead(); + popBuilder(); + + pushBuilder(no_lah); + auto old_cur = builder()->addTmp("ocur", state().cur); + + // Parse value as an instance of the corresponding type. + auto x = pb->parseType(type, production.meta().field(), {}); + + // Compare parsed value against expected value. + auto no_match = + builder::or_(builder::equal(offset(old_cur), offset(state().cur)), builder::unequal(x, expected)); + + auto error = builder()->addIf(no_match); + pushBuilder(error); + builder()->addAssign(state().cur, old_cur); + pb->parseError(fmt("expecting %u", expected), meta); + popBuilder(); + + popBuilder(); + + builder()->addAssign(destination(type), expected); + return expected; + } + + case LiteralMode::Try: { + auto old_cur = builder()->addTmp("ocur", state().cur); + auto x = pb->parseTypeTry(type, production.meta().field(), {}); + auto new_cur = builder()->addTmp("ncur", state().cur); + builder()->addAssign(state().cur, old_cur); + + // Compare parsed value against expected value. + auto match = builder::and_(x, builder::and_(builder::unequal(offset(old_cur), offset(new_cur)), + builder::equal(builder::deref(x), expected))); + return builder::begin(builder::ternary(match, new_cur, old_cur)); + } + + default: util::cannot_be_reached(); + } + } + + result_t operator()(const hilti::ctor::UnsignedInteger& c) { + return parseInteger(c.type(), builder::expression(c), c.meta()); + } + + result_t operator()(const hilti::ctor::SignedInteger& c) { + return parseInteger(c.type(), builder::expression(c), c.meta()); + } +}; + +} // namespace + +Expression ParserBuilder::parseLiteral(const Production& p, const std::optional& dst) { + if ( auto e = Visitor(this, p, dst).dispatch(p.expression()) ) + return std::move(*e); + + hilti::logger().internalError(fmt("codegen: literal parser did not return expression for '%s'", p.expression())); +} diff --git a/spicy/src/compiler/codegen/parsers/types.cc b/spicy/src/compiler/codegen/parsers/types.cc new file mode 100644 index 000000000..c1fb1e184 --- /dev/null +++ b/spicy/src/compiler/codegen/parsers/types.cc @@ -0,0 +1,299 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace spicy; +using namespace spicy::detail; +using namespace spicy::detail::codegen; +using util::fmt; + +namespace builder = hilti::builder; + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + Visitor(ParserBuilder* pb_, const std::optional& field_, + const std::optional& dst_, bool is_try_) + : pb(pb_), field(field_), dst(dst_), is_try(is_try_) {} + ParserBuilder* pb; + const std::optional& field; + const std::optional& dst; + bool is_try; + + auto state() { return pb->state(); } + auto builder() { return pb->builder(); } + auto pushBuilder(std::shared_ptr b) { return pb->pushBuilder(std::move(b)); } + auto pushBuilder(std::shared_ptr b, const std::function& f) { + return pb->pushBuilder(std::move(b), f); + } + auto pushBuilder() { return pb->pushBuilder(); } + auto popBuilder() { return pb->popBuilder(); } + auto guardBuilder() { return pb->makeScopeGuard(); } + + Expression destination(const Type& t) { + if ( dst ) + return *dst; + + if ( field ) + return builder()->addTmp("x", field->parseType()); + + return builder()->addTmp("x", t); + } + + Expression performUnpack(const Expression& target, const Type& t, int len, std::vector unpack_args, + const Meta& m, bool is_try) { + if ( ! is_try ) { + auto error_msg = fmt("expecting %d bytes for unpacking value", len); + pb->waitForInput(builder::integer(len), error_msg, m); + + auto unpacked = builder::unpack(t, std::move(unpack_args)); + builder()->addAssign(builder::tuple({target, state().cur}), builder::deref(unpacked)); + pb->trimInput(); + return target; + } + else { + auto has_data = pb->waitForInputOrEod(builder::integer(len)); + + auto result = builder()->addTmp("result", type::Result(t)); + + auto true_ = builder()->addIf(has_data); + pushBuilder(true_); + auto unpacked = builder::deref(builder::unpack(t, std::move(unpack_args))); + builder()->addAssign(builder::tuple({result, state().cur}), unpacked); + popBuilder(); + + // TODO(bbannier): Initialize the error state of `result` with a + // proper message on an `else` branch. + return result; + } + } + + Expression fieldByteOrder() { + std::optional byte_order; + + if ( const auto& a = AttributeSet::find(field->attributes(), "&byte-order") ) + byte_order = *a->valueAs(); + else if ( const auto& p = state().unit.get().propertyItem("%byte-order") ) + byte_order = *p->expression(); + + + if ( byte_order ) + return builder::expect_type(std::move(*byte_order), builder::typeByID("spicy::ByteOrder")); + else + return builder::id("hilti::ByteOrder::Network"); + } + + result_t operator()(const hilti::type::Address& t) { + auto v4 = AttributeSet::find(field->attributes(), "&ipv4"); + auto v6 = AttributeSet::find(field->attributes(), "&ipv6"); + assert(! (v4 && v6)); + + if ( v4 ) + return performUnpack(destination(t), type::Address(), 4, + {state().cur, builder::id("hilti::AddressFamily::IPv4"), fieldByteOrder()}, t.meta(), + is_try); + + else + return performUnpack(destination(t), type::Address(), 16, + {state().cur, builder::id("hilti::AddressFamily::IPv6"), fieldByteOrder()}, t.meta(), + is_try); + } + + result_t operator()(const spicy::type::Bitfield& t) { + auto itype = hilti::type::UnsignedInteger(t.width(), t.meta()); + auto value = builder()->addTmp("bitfield", itype); + performUnpack(value, itype, t.width() / 8, {state().cur, fieldByteOrder()}, t.meta(), is_try); + + builder()->addDebugMsg("spicy", fmt("%s = %%s", field->id()), {value}); + builder()->addDebugIndent("spicy"); + + std::vector extracted_bits; + + for ( const auto& b : t.bits() ) { + auto bit_order = builder::id("spicy_rt::BitOrder::LSB0"); + + if ( const auto& a = AttributeSet::find(field->attributes(), "&bit-order") ) + bit_order = *a->valueAs(); + else if ( const auto& p = state().unit.get().propertyItem("%bit-order") ) + bit_order = *p->expression(); + + auto x = + builder()->addTmp("bits", itype, + builder::call("spicy_rt::extractBits", {value, builder::integer(b.lower()), + builder::integer(b.upper()), bit_order})); + + if ( auto a = AttributeSet::find(b.attributes(), "&convert") ) { + auto converted = builder()->addTmp(ID("converted"), b.type()); + auto block = builder()->addBlock(); + block->addLocal(ID("__dd"), itype, x); + block->addAssign(converted, *a->valueAs()); + x = converted; + } + + extracted_bits.push_back(x); + builder()->addDebugMsg("spicy", fmt("%s = %%s", b.id()), {x}); + } + + builder()->addDebugDedent("spicy"); + + auto target = destination(t.type()); + builder()->addAssign(target, builder::tuple(std::move(extracted_bits))); + return target; + } + + result_t operator()(const hilti::type::Real& t) { + auto type = AttributeSet::find(field->attributes(), "&type"); + assert(type); + return performUnpack(destination(t), type::Real(), 4, + {state().cur, *type->valueAs(), fieldByteOrder()}, t.meta(), is_try); + } + + result_t operator()(const hilti::type::SignedInteger& t) { + return performUnpack(destination(t), t, t.width() / 8, {state().cur, fieldByteOrder()}, t.meta(), is_try); + } + + result_t operator()(const hilti::type::UnsignedInteger& t) { + return performUnpack(destination(t), t, t.width() / 8, {state().cur, fieldByteOrder()}, t.meta(), is_try); + } + + result_t operator()(const hilti::type::Void& /* t */) { return hilti::expression::Void(); } + + result_t operator()(const hilti::type::Bytes& t) { + auto eod_attr = AttributeSet::find(field->attributes(), "&eod"); + auto size_attr = AttributeSet::find(field->attributes(), "&size"); + auto until_attr = AttributeSet::find(field->attributes(), "&until"); + auto chunked_attr = AttributeSet::find(field->attributes(), "&chunked"); + bool parse_attr = false; + + if ( (AttributeSet::find(field->attributes(), "&parse-from") || + AttributeSet::find(field->attributes(), "&parse-at")) && + ! (size_attr || until_attr) ) + parse_attr = true; + + auto target = destination(t); + + if ( eod_attr || parse_attr || size_attr ) { + pb->enableDefaultNewValueForField(false); + + auto new_data = [&]() { + auto have_data = builder()->addIf(builder::size(state().cur)); + pushBuilder(have_data, [&]() { + builder()->addAssign(target, state().cur); + pb->advanceInput(builder::size(state().cur)); + + if ( field ) + pb->newValueForField(*field, target); + }); + }; + + auto check_size = [&](const auto& have) { + auto want = builder::coerceTo(*size_attr->valueAs(), type::UnsignedInteger(64)); + auto insufficient = builder()->addIf(builder::unequal(have, want)); + pushBuilder(insufficient); + pb->parseError("insufficient input for &size", size_attr->meta()); + popBuilder(); + }; + + if ( chunked_attr ) { + std::optional orig_begin; + + if ( size_attr && ! eod_attr ) + orig_begin = builder()->addTmp("orig_begin", builder::begin(state().cur)); + + auto loop = builder()->addWhile(builder::bool_(true)); + pushBuilder(loop, [&]() { + builder()->addLocal("more_data", pb->waitForInputOrEod(builder::integer(1))); + new_data(); + auto at_eod = builder()->addIf(builder::not_(builder::id("more_data"))); + + pushBuilder(at_eod); + + if ( orig_begin ) + check_size(builder::difference(builder::begin(state().cur), *orig_begin)); + + builder()->addBreak(); + + popBuilder(); + }); + } + + else { + pb->waitForEod(); + + if ( size_attr && ! eod_attr ) + check_size(builder::size(state().cur)); + + new_data(); + } + + return target; + } + + if ( until_attr ) { + auto until_expr = builder::coerceTo(*until_attr->valueAs(), hilti::type::Bytes()); + auto until_bytes_var = builder()->addTmp("until_bytes", until_expr); + auto until_bytes_size_var = builder()->addTmp("until_bytes_sz", builder::size(until_bytes_var)); + + builder()->addAssign(target, builder::bytes("")); + auto body = builder()->addWhile(builder::bool_(true)); + pushBuilder(body, [&]() { + pb->waitForInput(until_bytes_size_var, "end-of-data reached before &until expression found", t.meta()); + + auto find = builder::memberCall(state().cur, "find", {until_bytes_var}); + auto found_id = ID("found"); + auto it_id = ID("it"); + auto found = builder::id(found_id); + auto it = builder::id(it_id); + builder()->addLocal(found_id, type::Bool()); + builder()->addLocal(it_id, type::stream::Iterator()); + builder()->addAssign(builder::tuple({found, it}), find); + builder()->addSumAssign(target, builder::memberCall(state().cur, "sub", {it})); + + auto [found_branch, not_found_branch] = builder()->addIfElse(found); + + pushBuilder(found_branch, [&]() { + pb->advanceInput(builder::sum(it, until_bytes_size_var)); + builder()->addBreak(); + }); + + pushBuilder(not_found_branch, [&]() { pb->advanceInput(it); }); + }); + + return target; + } + + return {}; + } +}; + +} // namespace + +Expression ParserBuilder::_parseType(const Type& t, const std::optional& field, + const std::optional& dst, bool is_try) { + assert(! is_try || (t.isA() || t.isA())); + + if ( auto e = Visitor(this, field, dst, is_try).dispatch(t) ) + return std::move(*e); + + hilti::logger().internalError(fmt("codegen: type parser did not return expression for '%s'", t)); +} + +Expression ParserBuilder::parseType(const Type& t, const std::optional& field, + const std::optional& dst) { + return _parseType(t, field, dst, /*is_try =*/false); +} + +Expression ParserBuilder::parseTypeTry(const Type& t, const std::optional& field, + const std::optional& dst) { + assert(t.isA() || t.isA()); + + return _parseType(t, field, dst, /*is_try =*/true); +} diff --git a/spicy/src/compiler/codegen/production.cc b/spicy/src/compiler/codegen/production.cc new file mode 100644 index 000000000..132393b29 --- /dev/null +++ b/spicy/src/compiler/codegen/production.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +using namespace spicy; +using namespace spicy::detail; + +bool codegen::production::nullable(const std::vector>& rhss) { + if ( rhss.empty() ) + return true; + + for ( const auto& rhs : rhss ) { + for ( auto& r : rhs ) { + if ( ! r.nullable() ) + goto next; + } + return true; + next: + continue; + } + + return false; +} + +std::string codegen::production::to_string(const Production& p) { + auto name = util::rsplit1(p.typename_(), "::").second; + + std::string can_sync; + std::string sync_at; + std::string kind; + std::string field; + std::string container; + + bool have_sync = p.meta().field() && AttributeSet::find(p.meta().field()->attributes(), "&synchronize"); + + if ( p.maySynchronize() || p.supportsSynchronize() || have_sync ) + can_sync = util::fmt(" (sync %c/%c/%c)", p.maySynchronize() ? '+' : '-', p.supportsSynchronize() ? '+' : '-', + have_sync ? '+' : '-'); + + std::string id = "n/a"; + + if ( p.isLiteral() ) + id = util::fmt("%" PRId64, p.tokenID()); + + if ( auto f = p.meta().field() ) { + std::string args; + + if ( auto x = f->arguments(); x.size() ) { + args = util::fmt(", args: (%s)", + util::join(util::transform(x, [](auto& a) { return util::fmt("%s", a); }), ", ")); + + field = util::fmt(" (field '%s', id %s, %s%s)", f->id(), id, to_string(f->engine()), args); + } + } + + if ( auto f = p.meta().container() ) + container = util::fmt(" (container '%s')", f->id()); + + return util::fmt("%10s: %-3s -> %s%s%s%s", name, p.symbol(), p.render(), field, container, can_sync); +} + +int64_t codegen::production::tokenID(const std::string& p) { + // We record the IDs in a global map to keep them stable. + static std::unordered_map ids; + + if ( auto i = ids.find(p); i != ids.end() ) + return i->second; + + return ids[p] = ids.size() + 1; +} diff --git a/spicy/src/compiler/codegen/productions/look-ahead.cc b/spicy/src/compiler/codegen/productions/look-ahead.cc new file mode 100644 index 000000000..638fc28c3 --- /dev/null +++ b/spicy/src/compiler/codegen/productions/look-ahead.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace spicy; +using namespace spicy::detail; + +bool codegen::production::LookAhead::supportsSynchronize() const { + if ( hasSize() ) + return true; + + for ( const auto& t : _lahs->first ) { + if ( ! t.supportsSynchronize() ) + return false; + } + + for ( const auto& t : _lahs->second ) { + if ( ! t.supportsSynchronize() ) + return false; + } + + return true; +} + +static std::string _fmtAlt(const codegen::Production& alt, const std::set& lahs) { + auto fmt = [&](const auto& lah) { + if ( lah.isLiteral() ) + return util::fmt("%s (id %" PRId64 ")", lah.render(), lah.tokenID()); + + return util::fmt("%s (not a literal)", lah.render()); + }; + + return util::fmt("{%s}: %s", util::join(util::transform(lahs, fmt), ", "), alt.symbol()); +} + +std::string codegen::production::LookAhead::render() const { + return _fmtAlt(_alternatives.first, _lahs->first) + " | " + _fmtAlt(_alternatives.second, _lahs->second); +} diff --git a/spicy/src/compiler/codegen/productions/switch.cc b/spicy/src/compiler/codegen/productions/switch.cc new file mode 100644 index 000000000..e1e81faa5 --- /dev/null +++ b/spicy/src/compiler/codegen/productions/switch.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +using namespace spicy; +using namespace spicy::detail; + +std::vector> codegen::production::Switch::rhss() const { + std::vector> rhss; + + for ( const auto& c : _cases ) + rhss.push_back({c.second}); + + if ( _default ) + rhss.push_back({*_default}); + + return rhss; +} + +std::string codegen::production::Switch::render() const { + std::string r; + + for ( const auto& c : _cases ) { + std::vector exprs; + + for ( const auto& e : c.first ) + exprs.push_back(util::fmt("%s", e)); + + if ( r.size() ) + r += " | "; + + r += util::fmt("[%s] -> %s", util::join(exprs, ","), c.second.symbol()); + } + + if ( _default ) { + if ( r.size() ) + r += " | "; + + r += util::fmt(" | * -> %s", _default->symbol()); + } + + return r; +} diff --git a/spicy/src/compiler/codegen/unit-builder.cc b/spicy/src/compiler/codegen/unit-builder.cc new file mode 100644 index 000000000..9eb82d0c8 --- /dev/null +++ b/spicy/src/compiler/codegen/unit-builder.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace spicy; +using namespace spicy::detail; + +using util::fmt; + +namespace builder = hilti::builder; + +namespace { + +struct FieldBuilder : public hilti::visitor::PreOrder { + FieldBuilder(CodeGen* cg, const spicy::type::Unit& unit) : cg(cg), unit(unit) {} + CodeGen* cg; + const spicy::type::Unit& unit; + std::vector fields; + + void addField(hilti::type::struct_::Field f) { fields.emplace_back(std::move(f)); } + + void operator()(const spicy::type::unit::item::Field& f, const position_t /* p */) { + if ( ! f.parseType().isA() ) { + // Create struct field. + AttributeSet attrs({Attribute("&optional")}); + + if ( auto x = AttributeSet::find(f.attributes(), "&default") ) + attrs = AttributeSet::add(attrs, *x); + + if ( f.isTransient() ) + // This field will never make it into the C++ struct. We still + // carry it around though as that makes type inference easier at + // times. + attrs = AttributeSet::add(attrs, Attribute("&no-emit")); + + auto nf = hilti::type::struct_::Field(f.id(), f.itemType(), std::move(attrs), f.meta()); + addField(std::move(nf)); + } + + // Add hooks. + auto addHookDeclaration = [&](bool foreach) { + if ( auto hook_decl = cg->compileHook(unit, f.id(), {f}, foreach, false, {}, {}, {}, f.meta()) ) { + auto nf = + hilti::type::struct_::Field(hook_decl->id().local(), hook_decl->function().type(), {}, f.meta()); + addField(std::move(nf)); + } + }; + + auto addHookImplementation = [&](auto& hook) { + if ( auto hook_impl = cg->compileHook(unit, ID(*unit.typeID(), f.id()), f, hook.isForEach(), hook.isDebug(), + hook.type().parameters(), hook.body(), hook.priority(), hook.meta()) ) + cg->addDeclaration(*hook_impl); + }; + + addHookDeclaration(false); + + if ( f.isContainer() ) + addHookDeclaration(true); + + for ( auto& h : f.hooks() ) + addHookImplementation(h); + } + + void operator()(const spicy::type::unit::item::Switch& f, const position_t /* p */) { + if ( f.cases().empty() ) + return; + + std::set seen; + + for ( const auto& [n, c] : util::enumerate(f.cases()) ) { + for ( const auto& i : c.items() ) { + if ( auto f = i.tryAs() ) { + if ( seen.find(f->id()) != seen.end() ) + // Validator ensures two fields with the same name are equivalent. + continue; + + seen.insert(f->id()); + } + + dispatch(i); + } + } + } + + void operator()(const spicy::type::unit::item::Variable& f, const position_t p) { + std::optional attrs; + auto ftype = f.itemType(); + + // Create struct field. + if ( auto x = f.default_() ) { + Node d = std::move(*x); + d.setScope(p.node.scope()); + attrs = AttributeSet::add(attrs, Attribute("&default", std::move(d))); + } + + if ( f.isOptional() ) + attrs = AttributeSet::add(attrs, Attribute("&optional")); + + auto nf = hilti::type::struct_::Field(f.id(), std::move(ftype), std::move(attrs), f.meta()); + addField(std::move(nf)); + } + + void operator()(const spicy::type::unit::item::Sink& s) { + auto type = builder::typeByID("spicy_rt::Sink", s.meta()); + AttributeSet attrs({Attribute("&default", builder::new_(std::move(type)))}); + + auto nf = hilti::type::struct_::Field(s.id(), type::Sink(), std::move(attrs), s.meta()); + addField(std::move(nf)); + } + + void operator()(const spicy::type::unit::item::UnitHook& h, const position_t /* p */) { + auto addHookImplementation = [&](const auto& hook, auto /* foreach */) { + if ( auto hook_impl = + cg->compileHook(unit, ID(*unit.typeID(), h.id()), {}, false, h.hook().isDebug(), + h.hook().type().parameters(), hook.body(), hook.priority(), hook.meta()) ) + cg->addDeclaration(*hook_impl); + }; + + addHookImplementation(h.hook(), AttributeSet::find(h.hook().attributes(), "foreach")); + } +}; + +} // anonymous namespace + +Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { + auto v = FieldBuilder(this, unit); + + for ( const auto& i : unit.items() ) + v.dispatch(i); + + auto add_hook = [&](std::string id, std::vector params) { + if ( auto hook_decl = + compileHook(unit, ID(std::move(id)), {}, false, false, std::move(params), {}, {}, unit.meta()) ) { + auto nf = + hilti::type::struct_::Field(hook_decl->id().local(), hook_decl->function().type(), {}, unit.meta()); + v.addField(std::move(nf)); + } + }; + + add_hook("0x25_init", {}); + add_hook("0x25_done", {}); + add_hook("0x25_error", {}); + + if ( unit.supportsSinks() ) { + add_hook("0x25_gap", {builder::parameter("seq", type::UnsignedInteger(64)), + builder::parameter("len", type::UnsignedInteger(64))}); + add_hook("0x25_overlap", {builder::parameter("seq", type::UnsignedInteger(64)), + builder::parameter("old", type::Bytes()), builder::parameter("new_", type::Bytes())}); + add_hook("0x25_skipped", {builder::parameter("seq", type::UnsignedInteger(64))}); + add_hook("0x25_undelivered", + {builder::parameter("seq", type::UnsignedInteger(64)), builder::parameter("data", type::Bytes())}); + } + + if ( unit.usesRandomAccess() ) { + auto f1 = hilti::type::struct_::Field(ID("__begin"), hilti::type::Optional(hilti::type::stream::Iterator())); + auto f2 = hilti::type::struct_::Field(ID("__position"), hilti::type::Optional(hilti::type::stream::Iterator())); + auto f3 = hilti::type::struct_::Field(ID("__position_update"), + hilti::type::Optional(hilti::type::stream::Iterator())); + v.addField(std::move(f1)); + v.addField(std::move(f2)); + v.addField(std::move(f3)); + } + + if ( unit.supportsSinks() || unit.isFilter() ) { + auto parser = hilti::type::struct_::Field(ID("__parser"), builder::typeByID("spicy_rt::Parser"), + AttributeSet({Attribute("&static"), Attribute("&internal")})); + v.addField(std::move(parser)); + } + + if ( unit.supportsSinks() ) { + auto sink = hilti::type::struct_::Field(ID("__sink"), builder::typeByID("spicy_rt::SinkState"), + AttributeSet({Attribute("&internal")})); + v.addField(std::move(sink)); + } + + if ( unit.supportsFilters() ) { + auto filters = hilti::type::struct_::Field(ID("__filters"), + hilti::type::StrongReference(builder::typeByID("spicy_rt::Filters")), + AttributeSet({Attribute("&internal")})); + v.addField(std::move(filters)); + } + + if ( unit.isFilter() ) { + auto forward = hilti::type::struct_::Field(ID("__forward"), + hilti::type::WeakReference(builder::typeByID("spicy_rt::Forward")), + AttributeSet({Attribute("&internal")})); + v.addField(std::move(forward)); + } + + auto ft = _pb.parseMethodFunctionType({}, unit.meta()); + v.addField(type::struct_::Field(type::struct_::Field("__parse_stage1", std::move(ft)))); + + assert(unit.typeID()); + Type s = hilti::type::Struct(unit.parameters(), std::move(v.fields)); + s = type::setTypeID(s, *unit.typeID()); + s = _pb.addParserMethods(s.as(), unit, declare_only); + + if ( unit.isPublic() || unit.isFilter() ) { + auto builder = builder::Builder(context()); + auto description = unit.propertyItem("%description"); + auto mime_types = util::transform(unit.propertyItems("%mime-type"), [](auto p) { return *p.expression(); }); + auto ports = util::transform(unit.propertyItems("%port"), [](auto p) { return *p.expression(); }); + + Expression parse1 = builder::null(); + if ( unit.parameters().empty() ) + parse1 = _pb.parseMethodExternalOverload1(unit); + + auto parser = + builder::struct_({{ID("name"), builder::string(*unit.typeID())}, + {ID("parse1"), parse1}, + {ID("parse2"), _pb.parseMethodExternalOverload2(unit)}, + {ID("description"), (description ? *description->expression() : builder::string(""))}, + {ID("mime_types"), + builder::vector(builder::typeByID("spicy_rt::MIMEType"), std::move(mime_types))}, + {ID("ports"), builder::vector(ports)}}, + unit.meta()); + + builder.addAssign(builder::id(ID(*unit.typeID(), "__parser")), parser); + + if ( unit.isPublic() ) + builder.addExpression( + builder::call("spicy_rt::registerParser", + {builder::id(ID(*unit.typeID(), "__parser")), builder::strong_reference(unit)})); + + auto register_unit = + builder::function(ID(fmt("__register_%s", util::replace(*unit.typeID(), "::", "_"))), type::Void(), {}, + builder.block(), type::function::Flavor::Standard, declaration::Linkage::Init); + addDeclaration(std::move(register_unit)); + } + + s.setOriginalNode(preserveNode(unit)); + return s; +} diff --git a/spicy/src/compiler/parser/driver.cc b/spicy/src/compiler/parser/driver.cc new file mode 100644 index 000000000..57d57a562 --- /dev/null +++ b/spicy/src/compiler/parser/driver.cc @@ -0,0 +1,126 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include +#include + +/** We compile with a source property to find this. */ +#include <__parser.h> + +using namespace spicy; +using namespace spicy::detail::parser; + +hilti::Result spicy::parseSource(std::istream& in, const std::string& filename) { + return Driver().parse(in, filename); +} + +hilti::Result spicy::parseExpression(const std::string& expr, const Meta& meta) { + spicy::detail::parser::Driver driver; + auto n = driver.parseExpression(expr, meta); + if ( ! n ) + return n.error(); + + return n->as(); +} + +namespace hilti::logging::debug { +inline const DebugStream Parser("parser"); +} // namespace hilti::logging::debug + +hilti::Result Driver::parse(std::istream& in, const std::string& filename) { + auto old_errors = hilti::logger().errors(); + _filename = filename; + _line = 1; + _next_token = Parser::token::START_MODULE; + + Scanner scanner(&in); + _scanner = &scanner; + + Parser parser(this); + _parser = &parser; + + hilti::logging::Stream dbg_stream_parser(hilti::logging::debug::Parser); + + if ( hilti::logger().isEnabled(hilti::logging::debug::Parser) ) { + _parser->set_debug_stream(dbg_stream_parser); + _parser->set_debug_level(1); + } + + _expression_mode = 1; + _scanner->enableExpressionMode(); + _parser->parse(); + + if ( hilti::logger().errors() > old_errors ) + return hilti::result::Error("parse error"); + + return hilti::to_node(_module); +} + +hilti::Result Driver::parseExpression(const std::string& expression, const Meta& m) { + auto old_errors = hilti::logger().errors(); + + if ( m.location() ) { + _filename = m.location().file(); + _line = m.location().from(); + } + else { + _filename = ""; + _line = 1; + } + + _next_token = Parser::token::START_EXPRESSION; + + std::stringstream str; + str << expression; + Scanner scanner(&str); + _scanner = &scanner; + + Parser parser(this); + _parser = &parser; + + hilti::logging::Stream dbg_stream_parser(hilti::logging::debug::Parser); + + if ( hilti::logger().isEnabled(hilti::logging::debug::Parser) ) { + _parser->set_debug_stream(dbg_stream_parser); + _parser->set_debug_level(1); + } + + _expression_mode = 1; + _scanner->enableExpressionMode(); + _parser->parse(); + + if ( hilti::logger().errors() > old_errors ) + return hilti::result::Error("parse error"); + + return hilti::to_node(_expression); +} + +int Driver::nextToken() { + int next = _next_token; + _next_token = 0; + return next; +} + +void Driver::error(const std::string& msg, const Meta& m) { hilti::logger().error(msg, m.location()); } + +void Driver::disablePatternMode() { _scanner->disablePatternMode(); } + +void Driver::enablePatternMode() { _scanner->enablePatternMode(); } + +void Driver::disableExpressionMode() { + if ( --_expression_mode == 0 ) + _scanner->disableExpressionMode(); +} + +void Driver::enableExpressionMode() { + if ( _expression_mode++ == 0 ) + _scanner->enableExpressionMode(); +} + +void Driver::disableDottedIDMode() { _scanner->disableDottedIDMode(); } + +void Driver::enableDottedIDMode() { _scanner->enableDottedIDMode(); } diff --git a/spicy/src/compiler/parser/parser.yy b/spicy/src/compiler/parser/parser.yy new file mode 100644 index 000000000..bb8fab1e8 --- /dev/null +++ b/spicy/src/compiler/parser/parser.yy @@ -0,0 +1,984 @@ +/* Copyright (c) 2020 by the Zeek Project. See LICENSE for details. */ + +%skeleton "lalr1.cc" /* -*- C++ -*- */ +%require "3.4" +%defines + +%{ +namespace spicy { namespace detail { class Parser; } } + +#include + +%} + +%locations +%initial-action +{ + @$.begin.filename = @$.end.filename = driver->currentFile(); + @$.begin.line = @$.end.line = driver->currentLine(); +}; + +%parse-param {class Driver* driver} +%lex-param {class Driver* driver} + +%define api.namespace {spicy::detail::parser} +%define api.parser.class {Parser} +%define parse.error verbose + +%debug +%verbose + +%glr-parser +%expect 173 +%expect-rr 140 + +%union {} +%{ + +#include + +#undef yylex +#define yylex driver->scanner()->lex + +static hilti::Meta toMeta(spicy::detail::parser::location l) { + return hilti::Meta(hilti::Location(*l.begin.filename, l.begin.line, l.end.line)); +} + +static hilti::Type iteratorForType(hilti::Type t, bool const_, hilti::Meta m) { + if ( hilti::type::isIterable(t) ) + return t.iteratorType(const_); + else { + hilti::logger().error(util::fmt("type '%s' is not iterable", t), m.location()); + return hilti::type::Error(m); + } +} + +static hilti::Type viewForType(hilti::Type t, hilti::Meta m) { + if ( hilti::type::isViewable(t) ) + return t.viewType(); + else { + hilti::logger().error(util::fmt("type '%s' is not viewable", t), m.location()); + return hilti::type::Error(m); + } +} + +#define __loc__ toMeta(yylhs.location) + +static int _field_width = 0; + +%} + +%token IDENT "identifier" +%token SCOPED_IDENT "scoped identifier" +%token DOTTED_IDENT "dotted identifier" +%token DOLLAR_IDENT "$-identifier" +%token ATTRIBUTE "attribute" +%token PROPERTY "property" + +%token CSTRING "string value" +%token CBYTES "bytes value" +%token CREGEXP "regular expression value" +%token CADDRESS "address value" +%token CPORT "port value" +%token CUREAL "real value" +%token CUINTEGER "unsigned integer value" +%token CBOOL "bool value" +%token CNULL "null value" + +%token EOD 0 "" + +%token ASSERT "assert" +%token ASSERT_EXCEPTION "assert-exception" +%token ADD +%token ADDRESS +%token AND +%token ANY +%token ARROW +%token AUTO +%token BITFIELD +%token BEGIN_ +%token BOOL +%token BREAK +%token BYTES +%token CADDR +%token CASE +%token CAST +%token CATCH +%token CLEAR +%token CONST +%token CONSTANT +%token CONTINUE +%token DEBUG_ +%token DECLARE +%token DEFAULT +%token DELETE +%token DIVIDEASSIGN +%token DOLLARDOLLAR +%token DOTDOT +%token REAL +%token ELSE +%token END_ +%token ENUM +%token EQ +%token EXCEPTION +%token EXPORT +%token FILE +%token FOR +%token FOREACH +%token FROM +%token FUNCTION +%token GEQ +%token GLOBAL +%token HASATTR +%token HOOK_COMPOSE +%token HOOK_PARSE +%token IF +%token IMPORT +%token IN +%token INOUT +%token INT +%token INT16 +%token INT32 +%token INT64 +%token INT8 +%token INTERVAL +%token ITERATOR +%token CONST_ITERATOR +%token LEQ +%token LIBRARY_TYPE "library type" +%token LIST +%token LOCAL +%token MAP +%token MARK +%token MINUSASSIGN +%token MINUSMINUS +%token MOD +%token MODULE +%token NEQ +%token NET +%token NETWORK +%token NEW +%token NONE +%token OBJECT +%token ON +%token OPTIONAL +%token OR +%token PLUSASSIGN +%token PLUSPLUS +%token PORT +%token POW +%token PRINT +%token PRIORITY +%token PRIVATE +%token PUBLIC +%token REGEXP +%token RETURN +%token SET +%token SHIFTLEFT +%token SHIFTRIGHT +%token SINK +%token STOP +%token STREAM "stream" +%token STRING +%token STRUCT +%token SWITCH +%token TIME +%token TIMER +%token TIMESASSIGN +%token TRY +%token TRYATTR +%token TUPLE +%token TYPE +%token UINT +%token UINT16 +%token UINT32 +%token UINT64 +%token UINT8 +%token UNIT +%token VAR +%token VECTOR +%token VIEW +%token VOID +%token WHILE + +%type local_id scoped_id dotted_id unit_hook_id +%type local_decl local_init_decl global_decl type_decl import_decl constant_decl function_decl global_scope_decl property_decl hook_decl +%type global_scope_items +%type base_type_no_attrs base_type type tuple_type struct_type enum_type unit_type bitfield_type +%type ctor tuple struct_ regexp list vector map set +%type expr tuple_elem member_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g +%type opt_tuple_elems1 opt_tuple_elems2 exprs opt_exprs opt_unit_field_args opt_unit_field_sinks +%type opt_else_block +%type opt_init_expression opt_unit_field_condition unit_field_repeat opt_unit_field_repeat opt_unit_switch_expr +%type function_with_body function_without_body +%type func_param +%type opt_func_param_kind +%type func_result opt_func_result +%type opt_func_flavor +%type opt_func_cc +%type opt_linkage +%type func_params opt_func_params opt_unit_params opt_unit_hook_params +%type stmt stmt_decl stmt_expr block braced_block +%type stmts opt_stmts +%type attribute unit_hook_attribute +%type opt_attributes opt_unit_hook_attributes +%type tuple_type_elem +%type tuple_type_elems +%type struct_field +%type struct_fields +%type struct_elems +%type struct_elem +%type map_elems opt_map_elems +%type map_elem +%type enum_label +%type enum_labels +%type bitfield_bits opt_bitfield_bits +%type bitfield_bits_spec +%type re_patterns +%type re_pattern_constant +%type switch_case +%type switch_cases +%type const_real +%type const_uint +%type const_sint + +// Spicy-only +%type opt_unit_field_id +%type opt_unit_field_engine opt_hook_engine +%type unit_hook +%type opt_unit_item_hooks unit_hooks +%type unit_item unit_variable unit_field unit_field_in_container unit_wide_hook unit_property unit_switch unit_sink +%type unit_items opt_unit_items +%type unit_switch_case +%type unit_switch_cases + +%% + +// Magic states sent by the scanner to provide two separate entry points. +%token START_MODULE START_EXPRESSION; +%start start; + +start : START_MODULE module + | START_EXPRESSION start_expr + ; + +start_expr : expr { driver->setDestinationExpression(std::move($1)); } + +module : MODULE local_id ';' + global_scope_items { auto m = hilti::Module($2, std::move($4.first), std::move($4.second), __loc__); + driver->setDestinationModule(std::move(m)); + } + ; + +/* IDs */ + +local_id : IDENT { std::string name($1); + + if (name.find('-') != std::string::npos) + hilti::logger().error(util::fmt("Invalid ID '%s': cannot contain '-'", name), __loc__.location()); + if (name.substr(0, 2) == "__") + hilti::logger().error(util::fmt("Invalid ID '%s': cannot start with '__'", name), __loc__.location()); + + $$ = hilti::ID(std::move(name), __loc__); + } + +scoped_id : local_id { $$ = std::move($1); } + | SCOPED_IDENT { $$ = hilti::ID($1, __loc__); } + +dotted_id : { driver->enableDottedIDMode(); } + DOTTED_IDENT + { driver->disableDottedIDMode(); } { $$ = hilti::ID($2, __loc__); } + +/* Declarations */ + +global_scope_items + : global_scope_items global_scope_decl + { $$ = std::move($1); $$.first.push_back($2); } + | global_scope_items stmt + { $$ = std::move($1); $$.second.push_back($2); } + | /* empty */ { } + ; + +global_scope_decl + : type_decl { $$ = std::move($1); } + | constant_decl { $$ = std::move($1); } + | global_decl { $$ = std::move($1); } + | function_decl { $$ = std::move($1); } + | import_decl { $$ = std::move($1); } + | property_decl { $$ = std::move($1); } + | hook_decl { $$ = std::move($1); } + +type_decl : opt_linkage TYPE scoped_id '=' type opt_attributes ';' + { $$ = hilti::declaration::Type(std::move($3), std::move($5), std::move($6), std::move($1), __loc__); } + +constant_decl : opt_linkage CONST scoped_id '=' expr ';' + { $$ = hilti::declaration::Constant($3, $5, $1, __loc__); } + +local_decl : LOCAL scoped_id '=' expr ';' { $$ = hilti::declaration::LocalVariable($2, $4.type(), $4, false, __loc__); } + | LOCAL scoped_id ':' type ';' { $$ = hilti::declaration::LocalVariable($2, $4, {}, false, __loc__); } + | LOCAL scoped_id ':' type '=' expr ';' + { $$ = hilti::declaration::LocalVariable($2, $4, $6, false, __loc__); } + ; + +local_init_decl + : LOCAL local_id ':' type '=' expr + { $$ = hilti::declaration::LocalVariable($2, $4, $6, false, __loc__); } + | LOCAL local_id '=' expr + { $$ = hilti::declaration::LocalVariable($2, $4, false, __loc__); } + ; + +global_decl : opt_linkage GLOBAL scoped_id '=' expr ';' + { $$ = hilti::declaration::GlobalVariable($3, $5.type(), $5, $1, __loc__); } + | opt_linkage GLOBAL scoped_id ':' type ';' + { $$ = hilti::declaration::GlobalVariable($3, $5, $1, __loc__); } + | opt_linkage GLOBAL scoped_id ':' type '=' expr ';' + { $$ = hilti::declaration::GlobalVariable($3, $5, $7, $1, __loc__); } + ; + +function_decl : opt_linkage function_with_body + { $$ = hilti::declaration::Function($2, $1, __loc__); } + | opt_linkage function_without_body ';' + { $$ = hilti::declaration::Function($2, $1, __loc__); } + ; + +import_decl : IMPORT local_id ';' { $$ = hilti::declaration::ImportedModule(std::move($2), std::string(".spicy"), __loc__); } + | IMPORT local_id FROM dotted_id ';' { $$ = hilti::declaration::ImportedModule(std::move($2), std::string(".spicy"), std::move($4), __loc__); } + ; + +property_decl : PROPERTY ';' { $$ = hilti::declaration::Property(ID(std::move($1)), __loc__); } + | PROPERTY '=' expr ';' { $$ = hilti::declaration::Property(ID(std::move($1)), std::move($3), __loc__); } + ; + +hook_decl : ON unit_hook_id unit_hook { ID unit = $2.namespace_(); + if ( unit.empty() ) + error(@$, "hook requires unit namespace"); + + auto hook = spicy::type::unit::item::UnitHook($2.local(), std::move($3), __loc__); + $$ = spicy::declaration::UnitHook($2, hilti::type::UnresolvedID(unit), std::move(hook), __loc__); + } + ; + +opt_linkage : PUBLIC { $$ = hilti::declaration::Linkage::Public; } + | PRIVATE { $$ = hilti::declaration::Linkage::Private; } + | /* empty */ { $$ = hilti::declaration::Linkage::Private; } + +/* Functions */ + +function_with_body + : FUNCTION opt_func_flavor opt_func_cc scoped_id '(' opt_func_params ')' opt_func_result opt_attributes braced_block + { + auto ftype = hilti::type::Function($8, $6, $2, __loc__); + $$ = hilti::Function($4, std::move(ftype), $10, $3, $9, __loc__); + } + +function_without_body + : FUNCTION opt_func_flavor opt_func_cc scoped_id '(' opt_func_params ')' opt_func_result opt_attributes + { + auto ftype = hilti::type::Function($8, $6, $2, __loc__); + $$ = hilti::Function($4, std::move(ftype), {}, $3, $9, __loc__); + } + + +opt_func_flavor : /* empty */ { $$ = hilti::type::function::Flavor::Standard; } + +opt_func_cc : CSTRING { try { + $$ = hilti::function::calling_convention::from_string($1); + } catch ( std::out_of_range& e ) { + error(@$, "unknown calling convention"); + } + } + | /* empty */ { $$ = hilti::function::CallingConvention::Standard; } + + +opt_func_params + : func_params { $$ = std::move($1); } + | /* empty */ { $$ = std::vector{}; } + +func_params : func_params ',' func_param { $$ = std::move($1); $$.push_back($3); } + | func_param { $$ = std::vector{$1}; } + +func_param : opt_func_param_kind local_id ':' type opt_init_expression + { $$ = hilti::type::function::Parameter($2, $4, $1, $5, __loc__); } + +func_result : ':' type { $$ = hilti::type::function::Result(std::move($2), __loc__); } + +opt_func_result : func_result { $$ = std::move($1); } + | /* empty */ { $$ = hilti::type::function::Result(hilti::type::Void(__loc__), __loc__); } + +opt_func_param_kind + : INOUT { $$ = hilti::declaration::parameter::Kind::InOut; } + | /* empty */ { $$ = hilti::declaration::parameter::Kind::In; } + ; + +opt_init_expression : '=' expr { $$ = std::move($2); } + | /* empty */ { $$ = {}; } + ; + +/* Statements */ + +block : braced_block { $$ = std::move($1); } + | stmt { $$ = hilti::statement::Block({$1}, __loc__); } + ; + +braced_block : '{' opt_stmts '}' { $$ = hilti::statement::Block(std::move($2), __loc__); } + +opt_stmts : stmts { $$ = std::move($1); } + | /* empty */ { $$ = std::vector{}; } + +stmts : stmts stmt { $$ = std::move($1); $$.push_back($2); } + | stmt { $$ = std::vector{std::move($1)}; } + +stmt : stmt_expr ';' { $$ = std::move($1); } + | stmt_decl { $$ = std::move($1); } + | ASSERT expr ';' { $$ = hilti::statement::Assert(std::move($2), {}, __loc__); } + | ASSERT expr ':' expr ';' { $$ = hilti::statement::Assert(std::move($2), std::move($4), __loc__); } + | ASSERT_EXCEPTION expr ';' { $$ = hilti::statement::Assert(hilti::statement::assert::Exception(), std::move($2), {}, {}, __loc__); } + | ASSERT_EXCEPTION expr ':' expr ';' + { $$ = hilti::statement::Assert(hilti::statement::assert::Exception(), std::move($2), {}, std::move($4), __loc__); } + | BREAK ';' { $$ = hilti::statement::Break(__loc__); } + | CONTINUE ';' { $$ = hilti::statement::Continue(__loc__); } + | FOR '(' local_id IN expr ')' block + { $$ = hilti::statement::For(std::move($3), std::move($5), std::move($7), __loc__); } + | IF '(' expr ')' block opt_else_block + { $$ = hilti::statement::If(std::move($3), std::move($5), std::move($6), __loc__); } + | IF '(' local_init_decl ')' block opt_else_block + { $$ = hilti::statement::If(std::move($3), {}, std::move($5), std::move($6), __loc__); } + | IF '(' local_init_decl ';' expr ')' block opt_else_block + { $$ = hilti::statement::If(std::move($3), std::move($5), std::move($7), std::move($8), __loc__); } + | PRINT opt_exprs ';' { $$ = spicy::statement::Print(std::move($2), __loc__); } + | RETURN ';' { $$ = hilti::statement::Return(__loc__); } + | RETURN expr ';' { $$ = hilti::statement::Return(std::move($2), __loc__); } + | STOP ';' { $$ = spicy::statement::Stop(__loc__); } + | SWITCH '(' expr ')' '{' switch_cases '}' + { $$ = hilti::statement::Switch(std::move($3), std::move($6), __loc__); } + | SWITCH '(' local_init_decl ')' '{' switch_cases '}' + { $$ = hilti::statement::Switch($3, hilti::expression::UnresolvedID($3.as().id()), std::move($6), __loc__); } + | WHILE '(' local_init_decl ';' expr ')' block opt_else_block + { $$ = hilti::statement::While(std::move($3), std::move($5), std::move($7), std::move($8), __loc__); } + | WHILE '(' expr ')' block opt_else_block + { $$ = hilti::statement::While(std::move($3), std::move($5), std::move($6), __loc__); } + | WHILE '(' local_init_decl ')' block opt_else_block + { $$ = hilti::statement::While(std::move($3), {}, std::move($5), std::move($6), __loc__); } + + | ADD expr ';' { auto op = $2.tryAs(); + if ( ! (op && op->kind() == hilti::operator_::Kind::Index) ) + error(@$, "'add' must be used with index expression only"); + + auto expr = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Add, op->operands(), __loc__); + $$ = hilti::statement::Expression(std::move(expr), __loc__); + } + + + | DELETE expr ';' { auto op = $2.tryAs(); + if ( ! (op && op->kind() == hilti::operator_::Kind::Index) ) + error(@$, "'add' must be used with index expression only"); + + auto expr = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Delete, op->operands(), __loc__); + $$ = hilti::statement::Expression(std::move(expr), __loc__); + } + ; + +opt_else_block + : ELSE block { $$ = std::move($2); } + | /* empty */ { $$ = {}; } + +switch_cases : switch_cases switch_case { $$ = std::move($1); $$.push_back(std::move($2)); } + | switch_case { $$ = std::vector({ std::move($1) }); } + +switch_case : CASE exprs ':' block { $$ = hilti::statement::switch_::Case(std::move($2), std::move($4), __loc__); } + | DEFAULT ':' block { $$ = hilti::statement::switch_::Case(hilti::statement::switch_::Default(), std::move($3), __loc__); } + +stmt_decl : local_decl { $$ = hilti::statement::Declaration($1, __loc__); } + | type_decl { $$ = hilti::statement::Declaration($1, __loc__); } + | constant_decl { $$ = hilti::statement::Declaration($1, __loc__); } + ; + +stmt_expr : expr { $$ = hilti::statement::Expression($1, __loc__); } + +/* Types */ + +base_type_no_attrs + : ANY { $$ = hilti::type::Any(__loc__); } + | ADDRESS { $$ = hilti::type::Address(__loc__); } + | BOOL { $$ = hilti::type::Bool(__loc__); } + | BYTES { $$ = hilti::type::Bytes(__loc__); } + | INTERVAL { $$ = hilti::type::Interval(__loc__); } + | NETWORK { $$ = hilti::type::Network(__loc__); } + | PORT { $$ = hilti::type::Port(__loc__); } + | REAL { $$ = hilti::type::Real(__loc__); } + | REGEXP { $$ = hilti::type::RegExp(__loc__); } + | STREAM { $$ = hilti::type::Stream(__loc__); } + | STRING { $$ = hilti::type::String(__loc__); } + | TIME { $$ = hilti::type::Time(__loc__); } + | VOID { $$ = hilti::type::Void(__loc__); } + + | INT8 { $$ = hilti::type::SignedInteger(8, __loc__); } + | INT16 { $$ = hilti::type::SignedInteger(16, __loc__); } + | INT32 { $$ = hilti::type::SignedInteger(32, __loc__); } + | INT64 { $$ = hilti::type::SignedInteger(64, __loc__); } + | UINT8 { $$ = hilti::type::UnsignedInteger(8, __loc__); } + | UINT16 { $$ = hilti::type::UnsignedInteger(16, __loc__); } + | UINT32 { $$ = hilti::type::UnsignedInteger(32, __loc__); } + | UINT64 { $$ = hilti::type::UnsignedInteger(64, __loc__); } + + | CONST_ITERATOR type_param_begin type type_param_end { $$ = iteratorForType(std::move($3), true, __loc__); } + | ITERATOR type_param_begin type type_param_end { $$ = iteratorForType(std::move($3), false, __loc__); } + | OPTIONAL type_param_begin type type_param_end { $$ = hilti::type::Optional($3, __loc__); } + | VIEW type_param_begin type type_param_end { $$ = viewForType(std::move($3), __loc__); } + + | LIST type_param_begin type type_param_end { $$ = hilti::type::Vector(std::move($3), __loc__); } + | MAP type_param_begin type ',' type type_param_end { $$ = hilti::type::Map(std::move($3), std::move($5), __loc__); } + | SET type_param_begin type type_param_end { $$ = hilti::type::Set(std::move($3), __loc__); } + | VECTOR type_param_begin type type_param_end { $$ = hilti::type::Vector(std::move($3), __loc__); } + + | SINK { $$ = spicy::type::Sink(__loc__); } + + | LIBRARY_TYPE '(' CSTRING ')' { $$ = hilti::type::Library(std::move($3), __loc__); } + + | tuple_type { $$ = std::move($1); } + | struct_type { $$ = std::move($1); } + | enum_type { $$ = std::move($1); } + | bitfield_type { $$ = std::move($1); } + | unit_type { $$ = std::move($1); } + + | type '&' { $$ = hilti::type::StrongReference(std::move($1), true, __loc__); } + ; + +base_type : base_type_no_attrs /* opt_attributes */ + { $$ = std::move($1); } + ; + +type : base_type { $$ = std::move($1); } + | scoped_id { $$ = hilti::type::UnresolvedID(std::move($1)); } + ; + +type_param_begin: + '<' + { driver->disableExpressionMode(); } + +type_param_end: + '>' + { driver->enableExpressionMode(); } + +tuple_type : TUPLE type_param_begin '*' type_param_end { $$ = hilti::type::Tuple(hilti::type::Wildcard(), __loc__); } + | TUPLE type_param_begin tuple_type_elems type_param_end { $$ = hilti::type::Tuple(std::move($3), __loc__); } + ; + +tuple_type_elems + : tuple_type_elems ',' tuple_type_elem + { $$ = std::move($1); $$.push_back(std::move($3)); } + | tuple_type_elem { $$ = std::vector>{ std::move($1) }; } + ; + +tuple_type_elem + : type { $$ = std::make_pair(hilti::ID(), std::move($1)); } + | local_id ':' type { $$ = std::make_pair(std::move($1), std::move($3)); } + ; + +struct_type : STRUCT '{' struct_fields '}' { $$ = hilti::type::Struct(std::move($3), __loc__); } + +struct_fields : struct_fields struct_field { $$ = std::move($1); $$.push_back($2); } + | /* empty */ { $$ = std::vector{}; } + +struct_field : type local_id opt_attributes ';' { $$ = hilti::type::struct_::Field(std::move($2), std::move($1), std::move($3), __loc__); } + +enum_type : ENUM '{' enum_labels '}' { $$ = hilti::type::Enum(std::move($3), __loc__); } + +enum_labels : enum_labels ',' enum_label { $$ = std::move($1); $$.push_back(std::move($3)); } + | enum_label { $$ = std::vector(); $$.push_back(std::move($1)); } + ; + +enum_label : local_id { $$ = hilti::type::enum_::Label(std::move($1), __loc__); } + | local_id '=' CUINTEGER { $$ = hilti::type::enum_::Label(std::move($1), $3, __loc__); } + ; + +bitfield_type : BITFIELD '(' const_uint ')' + { _field_width = $3; } + '{' opt_bitfield_bits '}' + { $$ = spicy::type::Bitfield($3, $7, __loc__); } + +opt_bitfield_bits + : bitfield_bits + { $$ = std::move($1); } + | /* empty */ { $$ = std::vector(); } + +bitfield_bits + : bitfield_bits bitfield_bits_spec + { $$ = std::move($1); $$.push_back(std::move($2)); } + | bitfield_bits_spec { $$ = std::vector(); $$.push_back(std::move($1)); } + +bitfield_bits_spec + : local_id ':' const_uint DOTDOT const_uint opt_attributes ';' + { $$ = spicy::type::bitfield::Bits(std::move($1), $3, $5, _field_width, std::move($6), __loc__); } + | local_id ':' const_uint opt_attributes ';' + { $$ = spicy::type::bitfield::Bits(std::move($1), $3, $3, _field_width, std::move($4), __loc__); } + +/* --- Begin of Spicy units --- */ + +unit_type : UNIT opt_unit_params '{' opt_unit_items '}' + { $$ = spicy::type::Unit(std::move($2), std::move($4), {}, __loc__); } + +opt_unit_params + : '(' opt_func_params ')' { $$ = std::move($2); } + | /* empty */ { $$ = std::vector{}; } + +unit_items : unit_items unit_item { $$ = std::move($1); $$.push_back(std::move($2)); } + | unit_item { $$ = std::vector(); $$.push_back($1); } + +opt_unit_items: unit_items { $$ = std::move($1);} + | /* empty */ { $$ = std::vector{}; } + + +unit_item : unit_field { $$ = std::move($1); } + | unit_variable { $$ = std::move($1); } + | unit_wide_hook { $$ = std::move($1); } + | unit_property { $$ = std::move($1); } + | unit_sink { $$ = std::move($1); } + | unit_switch { $$ = std::move($1); } + ; + + +unit_variable : VAR local_id ':' type opt_init_expression opt_attributes ';' + { $$ = spicy::type::unit::item::Variable(std::move($2), std::move($4), std::move($5), std::move($6), __loc__); } + +unit_sink : SINK local_id opt_attributes ';' { $$ = spicy::type::unit::item::Sink(std::move($2), std::move($3), __loc__); } + +unit_property : PROPERTY { $$ = type::unit::item::Property(ID(std::move($1)), false, __loc__); }; + | PROPERTY '=' expr ';' { $$ = type::unit::item::Property(ID(std::move($1)), std::move($3), false, __loc__); }; + +unit_field : opt_unit_field_id opt_unit_field_engine base_type opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks + { $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($3), std::move($2), {}, std::move($4), std::move($7), std::move($5), std::move($6), std::move($8), __loc__); } + + | opt_unit_field_id opt_unit_field_engine ctor opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks + { $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($3), std::move($2), {}, std::move($4), std::move($7), std::move($5), std::move($6), std::move($8), __loc__); } + + | opt_unit_field_id opt_unit_field_engine scoped_id opt_unit_field_args opt_unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks + { $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($3), std::move($2), std::move($4), std::move($5), std::move($8), std::move($6), std::move($7), std::move($9), __loc__); } + + | opt_unit_field_id opt_unit_field_engine '(' unit_field_in_container ')' unit_field_repeat opt_attributes opt_unit_field_condition opt_unit_field_sinks opt_unit_item_hooks + { $$ = spicy::type::unit::item::UnresolvedField(std::move($1), std::move($4), std::move($2), {}, std::move($6), std::move($9), std::move($7), std::move($8), std::move($10), __loc__); } + +unit_field_in_container + : ctor opt_unit_field_args opt_attributes + { $$ = spicy::type::unit::item::UnresolvedField({}, std::move($1), {}, std::move($2), {}, {}, std::move($3), {}, {}, __loc__); } + | scoped_id opt_unit_field_args opt_attributes + { $$ = spicy::type::unit::item::UnresolvedField({}, std::move($1), {}, std::move($2), {}, {}, std::move($3), {}, {}, __loc__); } + +unit_wide_hook : ON unit_hook_id unit_hook { $$ = spicy::type::unit::item::UnitHook(std::move($2), std::move($3), __loc__); } + +opt_unit_field_id + : local_id { $$ = std::move($1); } + | /* empty */ { $$ = std::nullopt; } + +opt_unit_field_engine + : ':' { $$ = spicy::Engine::All; } + | '<' { $$ = spicy::Engine::Parser; } + | '>' { $$ = spicy::Engine::Composer; } + | /* empty */ { $$ = spicy::Engine::All; } /* Default */ + +opt_unit_field_args + : '(' opt_exprs ')' { $$ = std::move($2); } + | /* empty */ { $$ = std::vector(); } + +unit_field_repeat + : '[' expr ']' { $$ = std::move($2); } + | '[' ']' { $$ = hilti::builder::null(); } + +opt_unit_field_repeat + : unit_field_repeat { $$ = std::move($1); } + | /* empty */ { $$ = {}; } + +opt_unit_field_condition + : IF '(' expr ')' { $$ = std::move($3); } + | /* empty */ { $$ = {}; } + +opt_unit_field_sinks + : ARROW exprs { $$ = std::move($2); } + | /* empty */ { $$ = std::vector(); } + +opt_unit_item_hooks + : unit_hooks { $$ = std::move($1); } + | ';' { $$ = std::vector(); } + +unit_hooks : unit_hooks unit_hook { $$ = std::move($1); $$.push_back(std::move($2)); } + | unit_hook { $$ = std::vector{std::move($1)}; } + +unit_hook : opt_unit_hook_params opt_unit_hook_attributes opt_hook_engine braced_block + { $$ = spicy::Hook(std::move($1), std::move($4), std::move($3), std::move($2), __loc__); } + +opt_unit_hook_params + : '(' opt_func_params ')' { $$ = std::move($2); } + | /* empty */ { $$ = std::vector{}; } + +opt_unit_hook_attributes + : opt_unit_hook_attributes unit_hook_attribute + { $$ = hilti::AttributeSet::add($1, $2); } + | /* empty */ { $$ = {}; } + +unit_hook_id : scoped_id { $$ = hilti::ID(util::replace($1, "%", "0x25_"), __loc__); } + | PROPERTY { $$ = hilti::ID(util::replace($1, "%", "0x25_"), __loc__); } /* for %init/%done */ + +unit_hook_attribute + : FOREACH { $$ = hilti::Attribute("foreach", __loc__); } + | PRIORITY '=' expr { $$ = hilti::Attribute("priority", std::move($3), __loc__); } + | PROPERTY { if ( $1 != "%debug" ) error(@$, "unexpected hook property, only %debug permitted"); + $$ = hilti::Attribute("%debug", __loc__); + } + +opt_hook_engine + : HOOK_COMPOSE { $$ = spicy::Engine::Composer; } + | HOOK_PARSE { $$ = spicy::Engine::Parser; } + | /* empty */ { $$ = spicy::Engine::Parser; } /* Default */ + +unit_switch : SWITCH opt_unit_switch_expr '{' unit_switch_cases '}' opt_unit_field_condition ';' + { $$ = spicy::type::unit::item::Switch(std::move($2), std::move($4), spicy::Engine::All, std::move($6), {}, __loc__); } + +opt_unit_switch_expr: '(' expr ')' { $$ = std::move($2); } + | /* empty */ { $$ = {}; } + +unit_switch_cases + : unit_switch_cases unit_switch_case + { $$ = std::move($1); $$.push_back(std::move($2)); } + | unit_switch_case { $$ = std::vector(); $$.push_back(std::move($1)); } + +unit_switch_case + : exprs ARROW '{' unit_items '}' { $$ = type::unit::item::switch_::Case($1, $4, __loc__); } + | '*' ARROW '{' unit_items '}' { $$ = type::unit::item::switch_::Case($4, __loc__); } + | exprs ARROW unit_item { $$ = type::unit::item::switch_::Case($1, {$3}, __loc__); } + | '*' ARROW unit_item { $$ = type::unit::item::switch_::Case(std::vector{$3}, __loc__); } + | unit_field { $$ = type::unit::item::switch_::Case($1, __loc__); } + +/* --- End of Spicy units --- */ + +/* Expressions */ + +expr : expr_0 { $$ = std::move($1); } + ; + +opt_exprs : exprs { $$ = std::move($1); } + | /* empty */ { $$ = std::vector(); } + +exprs : exprs ',' expr { $$ = std::move($1); $$.push_back(std::move($3)); } + | expr { $$ = std::vector{std::move($1)}; } + +expr_0 : expr_1 + ; + +expr_1 : expr_2 '=' expr_1 { $$ = hilti::expression::Assign(std::move($1), std::move($3), __loc__); } + | expr_2 MINUSASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DifferenceAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 PLUSASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::SumAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 TIMESASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::MultipleAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 DIVIDEASSIGN expr_1 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DivisionAssign, {std::move($1), std::move($3)}, __loc__); } + | expr_2 '?' expr ':' expr { $$ = hilti::expression::Ternary(std::move($1), std::move($3), std::move($5), __loc__); } + | expr_2 { $$ = std::move($1); } + +expr_2 : expr_2 OR expr_3 { $$ = hilti::expression::LogicalOr(std::move($1), std::move($3), __loc__); } + | expr_3 { $$ = std::move($1); } + +expr_3 : expr_3 AND expr_4 { $$ = hilti::expression::LogicalAnd(std::move($1), std::move($3), __loc__); } + | expr_4 { $$ = std::move($1); } + +expr_4 : expr_4 EQ expr_5 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Equal, {std::move($1), std::move($3)}, __loc__); } + | expr_4 NEQ expr_5 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Unequal, {std::move($1), std::move($3)}, __loc__); } + | expr_5 { $$ = std::move($1); } + +expr_5 : expr_5 '<' expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Lower, {std::move($1), std::move($3)}, __loc__); } + | expr_5 '>' expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Greater, {std::move($1), std::move($3)}, __loc__); } + | expr_5 GEQ expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::GreaterEqual, {std::move($1), std::move($3)}, __loc__); } + | expr_5 LEQ expr_6 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::LowerEqual, {std::move($1), std::move($3)}, __loc__); } + | expr_6 { $$ = std::move($1); } + +expr_6 : expr_6 '|' expr_7 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::BitOr, {std::move($1), std::move($3)}, __loc__); } + | expr_7 { $$ = std::move($1); } + +expr_7 : expr_7 '^' expr_8 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::BitXor, {std::move($1), std::move($3)}, __loc__); } + | expr_8 { $$ = std::move($1); } + +expr_8 : expr_8 '&' expr_9 { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::BitAnd, {std::move($1), std::move($3)}, __loc__); } + | expr_9 { $$ = std::move($1); } + +expr_9 : expr_9 SHIFTLEFT expr_a { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::ShiftLeft, {std::move($1), std::move($3)}, __loc__); } + | expr_9 SHIFTRIGHT expr_a { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::ShiftRight, {std::move($1), std::move($3)}, __loc__); } + | expr_a { $$ = std::move($1); } + +expr_a : expr_a '+' expr_b { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Sum, {std::move($1), std::move($3)}, __loc__); } + | expr_a '-' expr_b { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Difference, {std::move($1), std::move($3)}, __loc__); } + | expr_b { $$ = std::move($1); } + +expr_b : expr_b '%' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Modulo, {std::move($1), std::move($3)}, __loc__); } + | expr_b '*' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Multiple, {std::move($1), std::move($3)}, __loc__); } + | expr_b '/' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Division, {std::move($1), std::move($3)}, __loc__); } + | expr_b POW expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Power, {std::move($1), std::move($3)}, __loc__); } + | expr_c { $$ = std::move($1); } + +expr_c : '!' expr_c { $$ = hilti::expression::LogicalNot(std::move($2), __loc__); } + | '*' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Deref, {std::move($2)}, __loc__); } + | '~' expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Negate, {std::move($2)}, __loc__); } + | '|' expr_c '|' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Size, {std::move($2)}, __loc__); } + | MINUSMINUS expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DecrPrefix, {std::move($2)}, __loc__); } + | PLUSPLUS expr_c { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::IncrPrefix, {std::move($2)}, __loc__); } + | expr_d { $$ = std::move($1); } + +expr_d : expr_d '(' opt_exprs ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Call, {std::move($1), hilti::expression::Ctor(hilti::ctor::Tuple(std::move($3), __loc__))}, __loc__); } + | expr_d '.' member_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Member, {std::move($1), std::move($3)}, __loc__); } + | expr_d '.' member_expr '(' opt_exprs ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::MemberCall, {std::move($1), std::move($3), hilti::expression::Ctor(hilti::ctor::Tuple(std::move($5), __loc__))}, __loc__); } + | expr_d '[' expr ']' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Index, {std::move($1), std::move($3)}, __loc__); } + | expr_d HASATTR member_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::HasMember, {std::move($1), std::move($3)}, __loc__); } + | expr_d IN expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::In, {std::move($1), std::move($3)}, __loc__); } + | expr_d MINUSMINUS { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::DecrPostfix, {std::move($1)}, __loc__); } + | expr_d PLUSPLUS { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::IncrPostfix, {std::move($1)}, __loc__); } + | expr_d TRYATTR member_expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::TryMember, {std::move($1), std::move($3)}, __loc__); } + | expr_e { $$ = std::move($1); } + +expr_e : CAST type_param_begin type type_param_end '(' expr ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Cast, {std::move($6), hilti::expression::Type_(std::move($3))}, __loc__); } + | BEGIN_ '(' expr ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Begin, {std::move($3)}, __loc__); } + | END_ '(' expr ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::End, {std::move($3)}, __loc__); } + | NEW expr { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::New, {std::move($2), hilti::expression::Ctor(hilti::ctor::Tuple({}, __loc__))}, __loc__); } + | NEW scoped_id '(' opt_exprs ')' { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::New, {hilti::expression::UnresolvedID(std::move($2), __loc__), hilti::expression::Ctor(hilti::ctor::Tuple(std::move($4), __loc__))}, __loc__); } + | expr_f { $$ = std::move($1); } + +expr_f : ctor { $$ = hilti::expression::Ctor(std::move($1), __loc__); } + | '-' expr_g { $$ = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::SignNeg, {std::move($2)}, __loc__); } + | '[' expr FOR local_id IN expr ']' + { $$ = hilti::expression::ListComprehension(std::move($6), std::move($2), std::move($4), {}, __loc__); } + | '[' expr FOR local_id IN expr IF expr ']' + { $$ = hilti::expression::ListComprehension(std::move($6), std::move($2), std::move($4), std::move($8), __loc__); } + | expr_g + +expr_g : '(' expr ')' { $$ = std::move($2); } + | scoped_id { $$ = hilti::expression::UnresolvedID(std::move($1), __loc__); } + | DOLLARDOLLAR { $$ = hilti::expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, __loc__); } + +member_expr : local_id { $$ = hilti::expression::Member(std::move($1), __loc__); } + +/* Constants */ + +ctor : CADDRESS { $$ = hilti::ctor::Address(hilti::ctor::Address::Value($1), __loc__); } + | CADDRESS '/' CUINTEGER { $$ = hilti::ctor::Network(hilti::ctor::Network::Value($1, $3), __loc__); } + | CBOOL { $$ = hilti::ctor::Bool($1, __loc__); } + | CBYTES { $$ = hilti::ctor::Bytes(std::move($1), __loc__); } + | CPORT { $$ = hilti::ctor::Port(hilti::ctor::Port::Value($1), __loc__); } + | CNULL { $$ = hilti::ctor::Null(__loc__); } + | CSTRING { $$ = hilti::ctor::String($1, __loc__); } + + | CUREAL { $$ = hilti::ctor::Real($1, __loc__); } + | '+' CUREAL { $$ = hilti::ctor::Real($2, __loc__); } + | '-' CUREAL { $$ = hilti::ctor::Real(-$2, __loc__); } + | CUINTEGER { $$ = hilti::ctor::UnsignedInteger($1, 64, __loc__); } + | '+' CUINTEGER { $$ = hilti::ctor::UnsignedInteger($2, 64, __loc__); } + | '-' CUINTEGER { if ($2 > 0x8000000000000000) error(@$, "integer overflow on negation"); + $$ = hilti::ctor::SignedInteger(-$2, 64, __loc__); } + | OPTIONAL '(' expr ')' { $$ = hilti::ctor::Optional(std::move($3), __loc__); } + | INTERVAL '(' const_real ')' { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3), __loc__); } + | INTERVAL '(' const_sint ')' { $$ = hilti::ctor::Interval(hilti::ctor::Interval::Value($3), __loc__); } + | TIME '(' const_real ')' { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3), __loc__); } + | TIME '(' const_uint ')' { $$ = hilti::ctor::Time(hilti::ctor::Time::Value($3 * 1000000000), __loc__); } + | STREAM '(' CBYTES ')' { $$ = hilti::ctor::Stream(std::move($3), __loc__); } + + | UINT8 '(' CUINTEGER ')' { $$ = hilti::ctor::UnsignedInteger($3, 8, __loc__); } + | UINT16 '(' CUINTEGER ')' { $$ = hilti::ctor::UnsignedInteger($3, 16, __loc__); } + | UINT32 '(' CUINTEGER ')' { $$ = hilti::ctor::UnsignedInteger($3, 32, __loc__); } + | UINT64 '(' CUINTEGER ')' { $$ = hilti::ctor::UnsignedInteger($3, 64, __loc__); } + | INT8 '(' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger($3, 8, __loc__); } + | INT16 '(' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger($3, 16, __loc__); } + | INT32 '(' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger($3, 32, __loc__); } + | INT64 '(' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger($3, 64, __loc__); } + | INT8 '(' '-' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger(-$4, 8, __loc__); } + | INT16 '(' '-' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger(-$4, 16, __loc__); } + | INT32 '(' '-' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger(-$4, 32, __loc__); } + | INT64 '(' '-' CUINTEGER ')' { $$ = hilti::ctor::SignedInteger(-$4, 64, __loc__); } + + | list { $$ = std::move($1); } + | map { $$ = std::move($1); } + | regexp { $$ = std::move($1); } + | set { $$ = std::move($1); } + | struct_ { $$ = std::move($1); } + | tuple { $$ = std::move($1); } + | vector { $$ = std::move($1); } + ; + +const_real : CUREAL { $$ = $1; } + | '+' CUREAL { $$ = $2; } + | '-' CUREAL { $$ = -$2; } + +const_sint : CUINTEGER { $$ = $1; } + | '+' CUINTEGER { $$ = $2; } + | '-' CUINTEGER { if ( $2 > 0x8000000000000000 ) error(@$, "integer overflow on negation"); + $$ = -$2; + } + +const_uint : CUINTEGER { $$ = $1; } + | '+' CUINTEGER { $$ = $2; } + +tuple : '(' opt_tuple_elems1 ')' { $$ = hilti::ctor::Tuple(std::move($2), __loc__); } + +opt_tuple_elems1 + : tuple_elem ',' opt_tuple_elems2 { $$ = std::vector{std::move($1)}; $$.insert($$.end(), $3.begin(), $3.end()); } + | /* empty */ { $$ = std::vector(); } + +opt_tuple_elems2 + : tuple_elem ',' opt_tuple_elems2 { $$ = std::vector{std::move($1)}; $$.insert($$.end(), $3.begin(), $3.end()); } + | tuple_elem { $$ = std::vector{ std::move($1)}; } + | /* empty */ { $$ = std::vector(); } + + +tuple_elem : expr { $$ = std::move($1); } + | NONE { $$ = hilti::expression::Ctor(hilti::ctor::Null(__loc__), __loc__); } + +list : LIST '(' opt_exprs ')' { $$ = hilti::ctor::List(std::move($3), __loc__); } + | LIST type_param_begin type type_param_end '(' opt_tuple_elems1 ')' + { $$ = hilti::ctor::List(std::move($3), std::move($6), __loc__); } + +vector : '[' opt_exprs ']' { $$ = hilti::ctor::List(std::move($2), __loc__); } + | VECTOR '(' opt_exprs ')' { $$ = hilti::ctor::Vector(std::move($3), __loc__); } + | VECTOR type_param_begin type type_param_end '(' opt_tuple_elems1 ')' + { $$ = hilti::ctor::Vector(std::move($3), std::move($6), __loc__); } + +set : SET '(' opt_exprs ')' { $$ = hilti::ctor::Set(std::move($3), __loc__); } + | SET type_param_begin type type_param_end '(' opt_tuple_elems1 ')' + { $$ = hilti::ctor::Set(std::move($3), std::move($6), __loc__); } + +map : MAP '(' opt_map_elems ')' { $$ = hilti::ctor::Map(std::move($3), __loc__); } + | MAP type_param_begin type ',' type type_param_end '(' opt_map_elems ')' + { $$ = hilti::ctor::Map(std::move($3), std::move($5), std::move($8), __loc__); } + +struct_ : '[' struct_elems ']' { $$ = hilti::ctor::Struct(std::move($2), __loc__); } + /* We don't allow empty structs, we parse that as empty vectors instead. */ + +struct_elems : struct_elems ',' struct_elem { $$ = std::move($1); $$.push_back($3); } + | struct_elem { $$ = std::vector{ std::move($1) }; } + +struct_elem : '$' local_id '=' expr { $$ = hilti::ctor::struct_::Field(std::move($2), std::move($4)); } + +regexp : re_patterns opt_attributes { $$ = hilti::ctor::RegExp(std::move($1), std::move($2), __loc__); } + +re_patterns : re_patterns '|' re_pattern_constant + { $$ = $1; $$.push_back(std::move($3)); } + | re_pattern_constant { $$ = std::vector{std::move($1)}; } + +re_pattern_constant + : '/' { driver->enablePatternMode(); } CREGEXP { driver->disablePatternMode(); } '/' + { $$ = std::move($3); } + +opt_map_elems : map_elems { $$ = std::move($1); } + | /* empty */ { $$ = std::vector(); } + +map_elems : map_elems ',' map_elem { $$ = std::move($1); $$.push_back(std::move($3)); } + | map_elem { $$ = std::vector(); $$.push_back(std::move($1)); } + +map_elem : expr ':' expr { $$ = std::make_pair($1, $3); } + +/* Attributes */ + +attribute : ATTRIBUTE { $$ = hilti::Attribute(std::move($1), __loc__); } + | ATTRIBUTE '=' expr { $$ = hilti::Attribute(std::move($1), std::move($3), __loc__); } + +opt_attributes + : opt_attributes attribute { $$ = hilti::AttributeSet::add($1, $2); } + | /* empty */ { $$ = {}; } + +%% + +void spicy::detail::parser::Parser::error(const Parser::location_type& l, const std::string& m) { + driver->error(m, toMeta(l)); +} diff --git a/spicy/src/compiler/parser/scanner.ll b/spicy/src/compiler/parser/scanner.ll new file mode 100644 index 000000000..b287dcda3 --- /dev/null +++ b/spicy/src/compiler/parser/scanner.ll @@ -0,0 +1,257 @@ +/* Copyright (c) 2020 by the Zeek Project. See LICENSE for details. */ + +%{ +#include + +#include +#include + +using token = spicy::detail::parser::Parser::token; +using token_type = spicy::detail::parser::Parser::token_type; + +using namespace spicy; +using namespace spicy::detail::parser; + +#define yyterminate() return token::EOD + +%} + +%option c++ +%option prefix="Spicy" +%option noyywrap nounput batch debug yylineno + +%s EXPRESSION +%s IGNORE_NL + +%x DOTTED_ID +%x RE + +%{ +#define YY_USER_ACTION yylloc->columns(yyleng); + +static hilti::Meta toMeta(spicy::detail::parser::location l) { + return hilti::Meta(hilti::Location(*l.begin.filename, l.begin.line, l.end.line)); +} +%} + +address4 ({digits}"."){3}{digits} +address6 ("["({hexs}:){7}{hexs}"]")|("["0x{hexs}({hexs}|:)*"::"({hexs}|:)*"]")|("["({hexs}|:)*"::"({hexs}|:)*"]")|("["({hexs}|:)*"::"({hexs}|:)*({digits}"."){3}{digits}"]") + +attribute \&[a-zA-Z_][a-zA-Z_0-9-]* +blank [ \t] +comment [ \t]*#[^\n]*\n +digit [0-9] +digits {digit}+ +hexit [0-9a-fA-F] +hexs {hexit}+ +E ([Ee][+-]?{digits}) +P ([Pp][+-]?{digits}) +decfloat {digits}{E}|{digit}*\.{digits}{E}?|{digits}\.{digit}+{E}? +hexfloat 0[xX]({hexit}+{P}|{hexit}*\.{hexit}+{P}?|{hexit}+\.{hexit}+{P}?) +id [a-zA-Z_]|[a-zA-Z_][a-zA-Z_0-9-]*[a-zA-Z_0-9]|[$][$] +property %[a-zA-Z_][a-zA-Z_0-9-]* +string \"(\\.|[^\\"])*\" + +%% + +%{ + auto range_error_int = [d=driver,l=yylloc]{d->error("integer literal range error", toMeta(*l));}; + auto range_error_real = [d=driver,l=yylloc]{d->error("real literal range error", toMeta(*l));}; + + yylloc->step (); +%} + +%{ + int next = driver->nextToken(); + + if ( next ) + return (token_type)next; +%} + +{blank}+ yylloc->step(); +[\n]+ yylloc->lines(yyleng); yylloc->step(); +{comment} yylloc->lines(1); yylloc->step(); + +__library_type return token::LIBRARY_TYPE; +addr return token::ADDRESS; +add return token::ADD; +any return token::ANY; +assert return token::ASSERT; +assert-exception return token::ASSERT_EXCEPTION; +attribute return token::ATTRIBUTE; +begin return token::BEGIN_; +bitfield return token::BITFIELD; +bool return token::BOOL; +break return token::BREAK; +bytes return token::BYTES; +case return token::CASE; +cast return token::CAST; +catch return token::CATCH; +const return token::CONST; +const_iterator return token::CONST_ITERATOR; +constant return token::CONSTANT; +continue return token::CONTINUE; +cregexp return token::CREGEXP; +cstring return token::CSTRING; +default return token::DEFAULT; +delete return token::DELETE; +else return token::ELSE; +end return token::END_; +enum return token::ENUM; +exception return token::EXCEPTION; +export return token::EXPORT; +file return token::FILE; +for return token::FOR; +foreach return token::FOREACH; +from return token::FROM; +function return token::FUNCTION; +global return token::GLOBAL; +ident return token::IDENT; +if return token::IF; +import return token::IMPORT; +in return token::IN; +inout return token::INOUT; +int16 return token::INT16; +int32 return token::INT32; +int64 return token::INT64; +int8 return token::INT8; +interval return token::INTERVAL; +iterator return token::ITERATOR; +list return token::LIST; +local return token::LOCAL; +map return token::MAP; +mark return token::MARK; +mod return token::MOD; +module return token::MODULE; +net return token::NET; +new return token::NEW; +object return token::OBJECT; +on return token::ON; +optional return token::OPTIONAL; +port return token::PORT; +print return token::PRINT; +priority return token::PRIORITY; +private return token::PRIVATE; +property return token::PROPERTY; +public return token::PUBLIC; +real return token::REAL; +regexp return token::REGEXP; +return return token::RETURN; +set return token::SET; +sink return token::SINK; +stop return token::STOP; +stream return token::STREAM; +string return token::STRING; +struct return token::STRUCT; +switch return token::SWITCH; +time return token::TIME; +timer return token::TIMER; +try return token::TRY; +tuple return token::TUPLE; +type return token::TYPE; +uint16 return token::UINT16; +uint32 return token::UINT32; +uint64 return token::UINT64; +uint8 return token::UINT8; +unit return token::UNIT; +var return token::VAR; +vector return token::VECTOR; +view return token::VIEW; +void return token::VOID; +while return token::WHILE; + +!= return token::NEQ; +\&\& return token::AND; +\+= return token::PLUSASSIGN; +-- return token::MINUSMINUS; +-= return token::MINUSASSIGN; +\/= return token::DIVIDEASSIGN; +\*= return token::TIMESASSIGN; +\<\< return token::SHIFTLEFT; +\<= return token::LEQ; +== return token::EQ; +\>= return token::GEQ; +\?\. return token::HASATTR; +\.\? return token::TRYATTR; +\*\* return token::POW; +\+\+ return token::PLUSPLUS; +\|\| return token::OR; +\.\. return token::DOTDOT; +-> return token::ARROW; +!< return token::HOOK_PARSE; +!> return token::HOOK_COMPOSE; +\$\$ return token::DOLLARDOLLAR; +\>\> return token::SHIFTRIGHT; + + +False yylval->bool_ = 0; return token::CBOOL; +True yylval->bool_ = 1; return token::CBOOL; +None return token::NONE; +Null return token::CNULL; + +{attribute} yylval->str = yytext; return token::ATTRIBUTE; +{property} yylval->str = yytext; return token::PROPERTY; +{digits}\/(tcp|udp) yylval->str = yytext; return token::CPORT; +{address4} yylval->str = yytext; return token::CADDRESS; +{address6} yylval->str = std::string(yytext, 1, strlen(yytext) - 2); return token::CADDRESS; + +{digits}|0x{hexs} yylval->uint = util::chars_to_uint64(yytext, 0, range_error_int); return token::CUINTEGER; +{string} yylval->str = util::expandEscapes(std::string(yytext, 1, strlen(yytext) - 2)); return token::CSTRING; +b{string} yylval->str = util::expandEscapes(std::string(yytext, 2, strlen(yytext) - 3)); return token::CBYTES; +'.' yylval->uint = *(yytext +1); return token::CUINTEGER; + +{decfloat}|{hexfloat} yylval->real = util::chars_to_double(yytext, range_error_real); return token::CUREAL; + +{id} yylval->str = yytext; return token::IDENT; +{id}(::{id}){1,}(::{property})? yylval->str = yytext; return token::SCOPED_IDENT; +{id}(::{property})? yylval->str = yytext; return token::SCOPED_IDENT; +\${id} yylval->str = yytext + 1; return token::DOLLAR_IDENT; + +[][!$?.,=:;<>(){}/|*/&^%!+~-] return (token_type) yytext[0]; + +. driver->error("invalid character", toMeta(*yylloc)); + +(\\.|[^\\\/])* yylval->str = util::replace(yytext, "\\/", "/"); return token::CREGEXP; +[/\\\n] return (token_type) yytext[0]; + +{id}(\.{id})* yylval->str = yytext; return token::DOTTED_IDENT; +{blank}+ yylloc->step(); +[\n]+ yylloc->lines(yyleng); yylloc->step(); + +%% + +int SpicyFlexLexer::yylex() +{ + assert(false); // Shouldn't be called. + return 0; +} + +void spicy::detail::parser::Scanner::enablePatternMode() +{ + yy_push_state(RE); +} + +void spicy::detail::parser::Scanner::disablePatternMode() +{ + yy_pop_state(); +} + +void spicy::detail::parser::Scanner::enableExpressionMode() +{ + yy_push_state(EXPRESSION); +} + +void spicy::detail::parser::Scanner::disableExpressionMode() +{ + yy_pop_state(); +} + +void spicy::detail::parser::Scanner::enableDottedIDMode() +{ + yy_push_state(DOTTED_ID); +} + +void spicy::detail::parser::Scanner::disableDottedIDMode() +{ + yy_pop_state(); +} diff --git a/spicy/src/compiler/plugin.cc b/spicy/src/compiler/plugin.cc new file mode 100644 index 000000000..e2f48cba2 --- /dev/null +++ b/spicy/src/compiler/plugin.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include +#include + +using namespace spicy; +using namespace spicy::detail; + +static hilti::Plugin spicy_plugin() { + return hilti::Plugin{ + .component = "Spicy", + .extension = ".spicy", + .cxx_includes = {"spicy/rt/libspicy.h"}, + + .library_paths = + [](const std::shared_ptr& /* ctx */) { return spicy::configuration().spicy_library_paths; }, + + .parse = [](std::istream& in, const std::filesystem::path& path) { return parseSource(in, path); }, + + .coerce_ctor = [](Ctor c, const Type& dst, + bitmask style) { return detail::coerceCtor(std::move(c), dst, style); }, + + .coerce_type = [](Type t, const Type& dst, + bitmask style) { return detail::coerceType(std::move(t), dst, style); }, + + .build_scopes = [](const std::shared_ptr& /* ctx */, + const std::vector>& m, hilti::Unit* u) { buildScopes(m, u); }, + + .resolve_ids = [](const std::shared_ptr& /* ctx */, Node* n, + hilti::Unit* u) { return resolveIDs(n, u); }, + + .resolve_operators = [](const std::shared_ptr& /* ctx */, Node* /* n */, hilti::Unit * + /* u */) -> bool { return false; }, + + .apply_coercions = [](const std::shared_ptr& /* ctx */, Node* n, + hilti::Unit* u) { return applyCoercions(n, u); }, + + .pre_validate = [](const std::shared_ptr& /* ctx */, const Node& n, + hilti::Unit* u) { preTransformValidateAST(n, u); }, + + .post_validate = [](const std::shared_ptr& /* ctx */, const Node& n, + hilti::Unit* u) { postTransformValidateAST(n, u); }, + + .preserved_validate = [](const std::shared_ptr& /* ctx */, const std::vector& n, + hilti::Unit* u) { preservedValidateAST(n, u); }, + + .transform = [](std::shared_ptr ctx, Node* n, bool init, hilti::Unit* u) -> bool { + return CodeGen(std::move(ctx)).compileModule(n, init, u); + }, + + .print_ast = [](const Node& root, hilti::printer::Stream& out) { return printAST(root, out); }}; +} + +static hilti::plugin::Register _(spicy_plugin()); diff --git a/spicy/src/compiler/visitors/apply-coercions.cc b/spicy/src/compiler/visitors/apply-coercions.cc new file mode 100644 index 000000000..82e0353f9 --- /dev/null +++ b/spicy/src/compiler/visitors/apply-coercions.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace spicy; + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + // Currently nothing to do here. Note that we coerce field attributes on + // access through builder::coerceTo(). + + bool modified = false; + + Expression coerceToPending(const Expression& e, const Type& t) { + return hilti::expression::PendingCoerced(e, t, e.meta()); + } + + template + void replaceNode(position_t* p, T&& n) { + p->node = std::forward(n); + modified = true; + } +}; + +} // anonymous namespace + +bool spicy::detail::applyCoercions(Node* root, hilti::Unit* /* unit */) { + util::timing::Collector _("spicy/compiler/apply-coercions"); + + auto v = Visitor(); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return v.modified; +} diff --git a/spicy/src/compiler/visitors/coercer.cc b/spicy/src/compiler/visitors/coercer.cc new file mode 100644 index 000000000..e537370e0 --- /dev/null +++ b/spicy/src/compiler/visitors/coercer.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include + +using namespace spicy; + +namespace { + +struct VisitorCtor : public hilti::visitor::PreOrder, VisitorCtor> { + VisitorCtor(const Type& dst, bitmask style) : dst(dst), style(style) {} + const hilti::Type& dst; + bitmask style; +}; + +struct VisitorType : public hilti::visitor::PreOrder, VisitorType> { + VisitorType(const Type& dst, bitmask style) : dst(dst), style(style) {} + const hilti::Type& dst; + bitmask style; +}; + +} // anonymous namespace + +std::optional detail::coerceCtor(hilti::Ctor c, const hilti::Type& dst, + bitmask style) { + if ( auto nc = VisitorCtor(dst, style).dispatch(std::move(c)) ) + return *nc; + + return {}; +} + +std::optional detail::coerceType(hilti::Type t, const hilti::Type& dst, bitmask style) { + if ( auto nt = VisitorType(dst, style).dispatch(std::move(t)) ) + return *nt; + + return {}; +} diff --git a/spicy/src/compiler/visitors/id-resolver.cc b/spicy/src/compiler/visitors/id-resolver.cc new file mode 100644 index 000000000..0798ec327 --- /dev/null +++ b/spicy/src/compiler/visitors/id-resolver.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace spicy; + +namespace { + +template +auto resolveField(const type::unit::item::UnresolvedField& u, const T& t) { // TODO(google-runtime-references) + return type::unit::item::Field(u.fieldID(), std::move(t), u.engine(), u.arguments(), u.repeatCount(), u.sinks(), + u.attributes(), u.condition(), u.hooks(), u.meta()); +} + +struct Visitor1 : public hilti::visitor::PostOrder { + explicit Visitor1(hilti::Unit* unit) : unit(unit) {} + hilti::Unit* unit; + bool modified = false; + + template + void replaceNode(position_t* p, T&& n) { + p->node = std::forward(n); + modified = true; + } + + void operator()(const type::unit::item::UnresolvedField& u, position_t p) { + if ( auto id = u.unresolvedID() ) { + auto resolved = hilti::lookupID(*id, p); + + if ( ! resolved ) { + p.node.setError(resolved.error()); + return; + } + + if ( auto t = resolved->first->tryAs() ) { + // Because we're doing type resolution ourselves here, we + // need to account for any &on-heap attribute. Normally, + // HILTI would take care of that for us when resolving a + // type. + Type tt = hilti::type::ResolvedID(*id, NodeRef(resolved->first), u.meta()); + + if ( t->type().isA() || AttributeSet::has(t->attributes(), "&on-heap") ) + tt = hilti::type::ValueReference(tt); + + replaceNode(&p, resolveField(u, tt)); + return; + } + + if ( auto c = resolved->first->tryAs() ) { + if ( auto ctor = c->value().tryAs() ) + replaceNode(&p, resolveField(u, ctor->ctor())); + else + p.node.setError("field value must be a constant"); + + return; + } + + p.node.setError(util::fmt("field value must be a constant or type, but is a %s", + resolved->first->as().displayName())); + } + + else if ( auto c = u.ctor() ) + replaceNode(&p, resolveField(u, *c)); + + else if ( auto t = u.type() ) + replaceNode(&p, resolveField(u, *t)); + + else if ( auto i = u.item() ) + replaceNode(&p, resolveField(u, *i)); + + else + hilti::logger().internalError("no known typw for unresolved field"); + } + + void operator()(const type::Unit& n, position_t p) { + if ( auto t = p.parent().tryAs(); + ! t && ! p.parent(2).tryAs() ) + replaceNode(&p, hilti::type::UnresolvedID(*n.typeID(), p.node.meta())); + } +}; + +struct Visitor2 : public hilti::visitor::PostOrder { + explicit Visitor2(hilti::Unit* unit) : unit(unit) {} + hilti::Unit* unit; + bool modified = false; + + template + void replaceNode(position_t* p, T&& n) { + p->node = std::forward(n); + modified = true; + } + + void operator()(const hilti::expression::Keyword& n, position_t p) { + if ( n.kind() == hilti::expression::keyword::Kind::DollarDollar && n.type().isA() ) { + auto f = p.findParent(); + + if ( ! f ) + return; + + Type dd; + + if ( auto t = p.findParent() ) { + // Inside a field's hook. + if ( t->get().isForEach() ) + dd = type::Computed(hilti::builder::memberCall(hilti::builder::member(hilti::builder::id("self"), + f->get().id()), + "front", {}), + false); + else + dd = f->get().itemType(); + } + + else if ( auto a = p.findParent() ) { + // Inside an attribute expression. + if ( a->get().tag() == "&until" || a->get().tag() == "&until_including" || a->get().tag() == "&while" ) + dd = type::Computed(hilti::builder::memberCall(hilti::builder::member(hilti::builder::id("self"), + f->get().id()), + "front", {}), + false); + else { + dd = f->get().parseType(); + + if ( auto bf = dd.tryAs() ) + dd = type::UnsignedInteger(bf->width(), bf->meta()); + } + } + + else { + p.node.setError("$$ not supported here"); + return; + } + + replaceNode(&p, + hilti::expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, dd, p.node.meta())); + } + } + + void operator()(const type::Unit& n, position_t p) { + if ( auto t = p.parent().tryAs(); + ! t && ! p.parent(2).tryAs() ) + replaceNode(&p, hilti::type::UnresolvedID(*n.typeID(), p.node.meta())); + } +}; + +} // anonymous namespace + +bool spicy::detail::resolveIDs(hilti::Node* root, hilti::Unit* unit) { + util::timing::Collector _("spicy/compiler/id-resolver"); + + auto v1 = Visitor1(unit); + for ( auto i : v1.walk(root) ) + v1.dispatch(i); + + auto v2 = Visitor2(unit); + for ( auto i : v2.walk(root) ) + v2.dispatch(i); + + return v1.modified || v2.modified; +} diff --git a/spicy/src/compiler/visitors/operator-resolver.cc b/spicy/src/compiler/visitors/operator-resolver.cc new file mode 100644 index 000000000..18bdad593 --- /dev/null +++ b/spicy/src/compiler/visitors/operator-resolver.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace spicy; +using namespace hilti; + +namespace { + +struct Visitor : public visitor::PostOrder { + Visitor(hilti::Module* module) : module(module) {} + + hilti::Module* module; + bool modified = false; + + Expression argument(const Expression& args, int i) { + auto ctor = args.as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return ctor.as().value()[i]; + } + + template + void replaceNode(position_t& p, T&& n) { + auto x = p.node; + p.node = std::move(n); + p.node.setOriginalNode(module->preserve(x)); + modified = true; + } +}; + +} // anonymous namespace + +bool spicy::detail::resolveOperators(hilti::Node* root, hilti::Unit* unit) { + util::timing::Collector _("spicy/compiler/resolve-operators"); + + auto v = Visitor(&root->as()); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return v.modified; +} diff --git a/spicy/src/compiler/visitors/printer.cc b/spicy/src/compiler/visitors/printer.cc new file mode 100644 index 000000000..88c377076 --- /dev/null +++ b/spicy/src/compiler/visitors/printer.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +using namespace spicy; +using util::fmt; + +namespace { + +struct Visitor : hilti::visitor::PreOrder { + explicit Visitor(hilti::printer::Stream& out) : out(out) {} // NOLINT + + auto const_(const Type& t) { return (out.isCompact() && hilti::type::isConstant(t)) ? "const " : ""; } + + void operator()(const type::bitfield::Bits& n) { + out << " " << n.id() << ": "; + + if ( n.lower() == n.upper() ) + out << fmt("%u", n.lower()); + else + out << fmt("%u..%d", n.lower(), n.upper()); + + if ( n.attributes() ) + out << ' ' << *n.attributes(); + + out << ";" << out.newline(); + } + + void operator()(const type::Bitfield& n, position_t /* p */) { + if ( ! out.isExpandSubsequentType() ) { + if ( auto id = n.typeID() ) { + out << *id; + return; + } + } + + out.setExpandSubsequentType(false); + + out << const_(n) << fmt("bitfield(%d) {\n", n.width()); + + for ( const auto& f : n.bits() ) + out << f; + + out << "}"; + } + + void operator()(const type::Sink& /* n */) { out << "sink"; } + + void operator()(const type::Unit& n) { + if ( n.isWildcard() ) + out << "unit<*>"; + else { + out << "unit { XXX } "; + } + } + + hilti::printer::Stream& out; +}; + +} // anonymous namespace + +bool detail::printAST(const hilti::Node& root, hilti::printer::Stream& out) { + util::timing::Collector _("spicy/printer"); + + return Visitor(out).dispatch(root); +} diff --git a/spicy/src/compiler/visitors/scope-builder.cc b/spicy/src/compiler/visitors/scope-builder.cc new file mode 100644 index 000000000..3e56da112 --- /dev/null +++ b/spicy/src/compiler/visitors/scope-builder.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include + +using namespace spicy; +using namespace hilti; + +namespace { + +struct Visitor : public hilti::visitor::PostOrder { + explicit Visitor(hilti::Unit* unit) : unit(unit) {} + hilti::Unit* unit; +}; + +} // anonymous namespace + +void spicy::detail::buildScopes(const std::vector>& modules, hilti::Unit* unit) { + util::timing::Collector _("spicy/compiler/scope-builder"); + + for ( auto& [id, m] : modules ) { + auto v = Visitor(unit); + for ( auto i : v.walk(&*m) ) + v.dispatch(i); + } +} diff --git a/spicy/src/compiler/visitors/validator.cc b/spicy/src/compiler/visitors/validator.cc new file mode 100644 index 000000000..7a7501703 --- /dev/null +++ b/spicy/src/compiler/visitors/validator.cc @@ -0,0 +1,412 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace spicy; +using util::fmt; + +namespace { + +struct PreTransformVisitor : public hilti::visitor::PreOrder { + void operator()(const statement::Print& /* n */) { + // TODO(robin): . + } + + void operator()(const statement::Stop& n, const_position_t p) { + // Must be inside &foreach hook. + if ( auto x = p.findParent(); ! (x && x->get().isForEach()) ) + hilti::logger().error("'stop' can only be used inside a 'foreach' hook", n); + } + + void operator()(const type::Unit& n) { + for ( const auto& i : n.items() ) { + // TODO(robin): should maybe validate Property individually instead of within Unit validation? + if ( i.id().str() == "%random-access" ) { + if ( i.expression() ) + hilti::logger().error("%random-access does not accept an argument", i); + } + + else if ( i.id().str() == "%filter" ) { + if ( i.expression() ) + hilti::logger().error("%filter does not accept an argument", i); + } + + else if ( i.id().str() == "%byte-order" ) { + if ( ! i.expression() ) + hilti::logger().error("%byte-order requires an expression", i); + + // expression type is checked by code generater + } + + else if ( i.id().str() == "%description" ) { + if ( ! i.expression() ) { + hilti::logger().error("%description requires an argument", i); + return; + } + + if ( ! i.expression()->type().isA() ) + hilti::logger().error("%description requires a string argument", i); + } + + else if ( i.id().str() == "%mime-type" ) { + if ( ! i.expression() ) { + hilti::logger().error("%mime-type requires an argument", i); + return; + } + + if ( ! i.expression()->type().isA() ) + hilti::logger().error("%mime-type requires a string argument", i); + + if ( auto x = i.expression()->tryAs() ) { + auto mt = x->ctor().as().value(); + + if ( ! spicy::rt::MIMEType::parse(mt) ) + hilti::logger().error("%mime-type argument must follow \"main/sub\" form", i); + } + } + + else if ( i.id().str() == "%port" ) { + if ( ! i.expression() ) { + hilti::logger().error("%ports requires an argument", i); + return; + } + + if ( ! i.expression()->type().tryAs() ) + hilti::logger().error("%port requires a port as its argument", i); + } + + else + hilti::logger().error(fmt("unknown property '%s'", i.id().str()), i); + } + } + + void operator()(const Attribute& a, const_position_t p) { + auto getAttrField = [](const_position_t p) -> std::optional { + try { + // Expected parent is AttributeSet whose expected parent is Field. + auto n = p.parent(2); + return n.tryAs(); + } catch ( std::out_of_range& ) { + } + + return {}; + }; + + if ( a.tag() == "&size" && ! a.hasValue() ) + hilti::logger().error("&size must provide an expression", a); + + else if ( a.tag() == "&default" && ! a.hasValue() ) + hilti::logger().error("%default requires an argument", a); + + else if ( a.tag() == "&eod" ) { + if ( auto f = getAttrField(p) ) { + if ( ! f->parseType().isA() || f->ctor() ) + hilti::logger().error("&eod is only valid for bytes fields", a); + } + else + hilti::logger().error("&eod is only valid for bytes fields", a); + } + + else if ( a.tag() == "&until" ) { + if ( auto f = getAttrField(p) ) { + if ( ! (f->parseType().isA() || f->parseType().isA()) ) + hilti::logger().error("&until is only valid for fields of type bytes or vector", a); + else if ( ! a.hasValue() ) + hilti::logger().error("&until must provide an expression", a); + } + else + hilti::logger().error("&until is only valid for fields of type bytes or vector", a); + } + + else if ( a.tag() == "&while" || a.tag() == "&until_including" ) { + if ( auto f = getAttrField(p) ) { + if ( ! f->parseType().isA() ) + hilti::logger().error(fmt("%s is only valid for fields of type bytes or vector", a.tag()), a); + else if ( ! a.hasValue() ) + hilti::logger().error(fmt("%s must provide an expression", a.tag()), a); + } + else + hilti::logger().error(fmt("%s is only valid for fields of type bytes or vector", a.tag()), a); + } + + else if ( a.tag() == "&chunked" ) { + if ( auto f = getAttrField(p) ) { + if ( ! f->parseType().isA() || f->ctor() ) + hilti::logger().error("&chunked is only valid for bytes fields", a); + else if ( a.hasValue() ) + hilti::logger().error("&chunked cannot have an expression", a); + else if ( ! (AttributeSet::has(f->attributes(), "&eod") || + AttributeSet::has(f->attributes(), "&size")) ) + hilti::logger().error("&chunked must be used with &eod or &size", a); + } + else + hilti::logger().error("&chunked is only valid for bytes fields", a); + } + + else if ( a.tag() == "&convert" ) { + if ( ! a.hasValue() ) + hilti::logger().error("&convert must provide an expression", a); + } + + else if ( a.tag() == "&transient" ) + hilti::logger() + .error("&transient is no longer available, use an anonymous field instead to achieve the same effect", + a); + + else if ( a.tag() == "&parse-from" ) { + if ( auto f = getAttrField(p) ) { + if ( ! a.hasValue() ) + hilti::logger().error("&parse-from must provide an expression", a); + else if ( auto e = a.valueAs(); + e && e->type() != type::unknown && e->type() != type::Bytes() ) + hilti::logger() + .error("&parse-from must have an expression of type either bytes or iterator", a); + } + } + + else if ( a.tag() == "&parse-at" ) { + if ( auto f = getAttrField(p) ) { + if ( ! a.hasValue() ) + hilti::logger().error("&parse-at must provide an expression", a); + else if ( auto e = a.valueAs(); + e && e->type() != type::unknown && e->type() != type::stream::Iterator() ) + hilti::logger().error("&parse-at must have an expression of type iterator", a); + } + } + } + + void operator()(const spicy::type::unit::item::Field& f) { + auto repeat = f.repeatCount(); + auto size_attr = AttributeSet::find(f.attributes(), "&size"); + auto count_attr = AttributeSet::find(f.attributes(), "&count"); + auto parse_from_attr = AttributeSet::find(f.attributes(), "&parse-from"); + auto parse_at_attr = AttributeSet::find(f.attributes(), "&parse-at"); + + if ( count_attr && (repeat && ! repeat->type().isA()) ) + hilti::logger().error("cannot have both `[..]` and &count", f); + + if ( parse_from_attr && parse_at_attr ) + hilti::logger().error("cannot have both &parse-from and &parse-at", f); + + if ( f.parseType().isA() && ! f.ctor() ) { + auto eod_attr = AttributeSet::find(f.attributes(), "&eod"); + auto until_attr = AttributeSet::find(f.attributes(), "&until"); + + if ( eod_attr ) { + if ( until_attr ) + hilti::logger().error("&eod incompatible with &until", f); + } + + else if ( ! until_attr && ! size_attr && ! parse_from_attr && ! parse_at_attr ) + hilti::logger().error("bytes field requires one of &size, &eod, or &until", f); + } + + if ( f.parseType().isA() ) { + auto v4 = AttributeSet::find(f.attributes(), "&ipv4"); + auto v6 = AttributeSet::find(f.attributes(), "&ipv6"); + + if ( ! (v4 || v6) ) + hilti::logger().error("address field must come with either &ipv4 or &ipv6 attribute", f); + + if ( v4 && v6 ) + hilti::logger().error("address field cannot have both &ipv4 and &ipv6 attributes", f); + } + + if ( f.parseType().isA() ) { + auto type = AttributeSet::find(f.attributes(), "&type"); + + if ( type ) { + if ( auto t = type->valueAs()->type().tryAs(); + ! (t && t->cxxID() && *t->cxxID() == ID("hilti::rt::real::Type")) ) + hilti::logger().error("&type attribute must be a spicy::RealType", f); + } + else + hilti::logger().error("field of type real must with a &type attribute", f); + } + + if ( f.sinks().size() && ! f.parseType().isA() ) + hilti::logger().error("only a bytes field can have sinks attached", f); + } + + void operator()(const spicy::type::unit::item::Switch& s) { + if ( s.cases().empty() ) { + hilti::logger().error("switch without cases", s); + return; + } + + int defaults = 0; + std::vector seen_exprs; + std::vector seen_fields; + + for ( const auto& c : s.cases() ) { + if ( c.items().empty() ) + hilti::logger().error("switch case without any item", c); + + if ( c.isDefault() ) + ++defaults; + + if ( s.expression() && ! c.isDefault() && c.expressions().empty() ) { + hilti::logger().error("case without expression", c); + break; + } + + if ( ! s.expression() && c.expressions().size() ) { + hilti::logger().error("case does not expect expression", c); + break; + } + + for ( const auto& e : c.expressions() ) { + for ( const auto& x : seen_exprs ) { + if ( e == x ) { + hilti::logger().error("duplicate case", e); + break; + } + } + + seen_exprs.emplace_back(e); + } + + for ( const auto& i : c.items() ) { + if ( auto f = i.tryAs() ) { + for ( const auto& x : seen_fields ) { + if ( f->id() == x.id() && (f->itemType() != x.itemType()) ) { + hilti::logger().error(fmt("field '%s' defined multiple times with different types", + f->id()), + i); + break; + } + } + + seen_fields.emplace_back(*f); + } + } + } + + if ( defaults > 1 ) + hilti::logger().error("more than one default case", s); + } + + void operator()(const spicy::type::unit::item::Variable& v) { + if ( v.itemType().isA() ) + hilti::logger().error( + "cannot use type 'sink' for unit variables; use either a 'sink' item or a reference to a sink " + "('sink&')", + v); + } +}; + +struct PostTransformVisitor : public hilti::visitor::PreOrder {}; + +struct PreservedVisitor : public hilti::visitor::PreOrder { + auto methodArgument(const hilti::expression::ResolvedOperatorBase& o, int i) { + auto ctor = o.op2().as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return ctor.as().value()[i]; + } + + void operator()(const operator_::sink::Connect& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->supportsSinks() ) + hilti::logger().error("unit type does not support sinks", n); + } + + void operator()(const operator_::sink::ConnectMIMETypeBytes& n) { + if ( auto x = n.op0().type().originalNode()->tryAs() ) { + if ( ! x->supportsSinks() ) + hilti::logger().error("unit type does not support sinks", n); + + if ( x->parameters().size() ) + hilti::logger().error("unit types with parameters cannot be connected through MIME type", n); + } + } + + void operator()(const operator_::sink::ConnectMIMETypeString& n) { + if ( auto x = n.op0().type().originalNode()->tryAs() ) { + if ( ! x->supportsSinks() ) + hilti::logger().error("unit type does not support sinks", n); + + if ( x->parameters().size() ) + hilti::logger().error("unit types with parameters cannot be connected through MIME type", n); + } + } + + void operator()(const operator_::unit::ConnectFilter& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->supportsFilters() ) + hilti::logger().error("unit type does not support filters", n); + + if ( auto y = methodArgument(n, 0) + .type() + .as() + .dereferencedType() + .originalNode() + ->as(); + ! y.isFilter() ) + hilti::logger().error("unit type cannot be a filter, %filter missing", n); + } + + void operator()(const operator_::unit::Forward& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->isFilter() ) + hilti::logger().error("unit type cannot be a filter, %filter missing", n); + } + + void operator()(const operator_::unit::ForwardEod& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->isFilter() ) + hilti::logger().error("unit type cannot be a filter, %filter missing", n); + } + + void operator()(const operator_::unit::Input& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + hilti::logger().error("use of 'input()' requires unit type to have property `%random-access`", n); + } + + void operator()(const operator_::unit::Offset& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + hilti::logger().error("use of 'offset()' requires unit type to have property `%random-access`", n); + } + + void operator()(const operator_::unit::SetInput& n) { + if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + hilti::logger().error("use of 'set_input()' requires unit type to have property `%random-access`", n); + } +}; + +} // anonymous namespace + +void spicy::detail::preTransformValidateAST(const Node& root, hilti::Unit* /* unit */) { + util::timing::Collector _("spicy/compiler/validator"); + + auto v = PreTransformVisitor(); + for ( auto i : v.walk(root) ) + v.dispatch(i); +} + +void spicy::detail::postTransformValidateAST(const Node& root, hilti::Unit* /* unit */) { + util::timing::Collector _("spicy/compiler/validator"); + + auto v = PostTransformVisitor(); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + hilti::reportErrorsInAST(root); +} + +void spicy::detail::preservedValidateAST(const std::vector& nodes, hilti::Unit* /* unit */) { + util::timing::Collector _("spicy/compiler/validator"); + + auto v = PreservedVisitor(); + for ( const auto& root : nodes ) { + for ( auto i : v.walk(root) ) + v.dispatch(i); + } +} diff --git a/spicy/src/config.cc.in b/spicy/src/config.cc.in new file mode 100644 index 000000000..df5ff3522 --- /dev/null +++ b/spicy/src/config.cc.in @@ -0,0 +1,89 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include + +using namespace spicy; + +const auto flatten = util::flattenParts; +const auto prefix = util::prefixParts; + +template +inline auto join(const std::vector& v1, const std::vector& v2) { + std::vector n; + + n.reserve(v1.size() + v2.size()); + + for ( const auto& i : v1 ) + n.push_back(i); + + for ( const auto& i : v2 ) + n.push_back(i); + + return n; +} + +void Configuration::extendHiltiConfiguration() { + auto& hlt = hilti::configuration(); + auto& spcy = spicy::configuration(); + spcy.init(hlt.uses_build_directory); + + hlt.hilti_library_paths = join(spcy.spicy_library_paths, hlt.hilti_library_paths); + hlt.runtime_cxx_flags_debug = join(spcy.runtime_cxx_flags_debug, hlt.runtime_cxx_flags_debug); + hlt.runtime_ld_flags_debug = join(spcy.runtime_ld_flags_debug, hlt.runtime_ld_flags_debug); + hlt.runtime_cxx_flags_release = join(spcy.runtime_cxx_flags_release, hlt.runtime_cxx_flags_release); + hlt.runtime_ld_flags_release = join(spcy.runtime_ld_flags_release, hlt.runtime_ld_flags_release); +} + +Configuration::Configuration() { + init(false); +} + +void Configuration::init(bool use_build_directory) { + uses_build_directory = use_build_directory; + std::string installation_tag = (use_build_directory ? "BUILD" : "INSTALL"); + + spicyc = (uses_build_directory ? "${PROJECT_BINARY_DIR}/bin/spicyc" : "${CMAKE_INSTALL_PREFIX}/bin/spicyc"); + + std::vector library_paths; + + if ( auto path = std::getenv("SPICY_PATH") ) { + library_paths = util::transform(util::split(path, ":"), [](auto s) { return std::string(s); }); + } + else { + library_paths = flatten({".", prefix("${SPICY_CONFIG_LIBRARY_DIRS}", "", installation_tag)}); + } + + spicy_library_paths = util::transform(library_paths, [](auto s) { return std::filesystem::path(s); }); + + runtime_cxx_flags_debug = flatten({ + prefix("${SPICY_CONFIG_RUNTIME_INCLUDE_DIRS_DEBUG}", "-I", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_CXX_FLAGS_DEBUG}", "", installation_tag) + }); + + runtime_cxx_flags_release = flatten({ + prefix("${SPICY_CONFIG_RUNTIME_INCLUDE_DIRS_RELEASE}", "-I", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_CXX_FLAGS_RELEASE}", "", installation_tag) + }); + + runtime_ld_flags_debug = flatten({ + prefix("${SPICY_CONFIG_RUNTIME_LIBRARY_DIRS_DEBUG}", "-L", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_LIBRARY_DIRS_DEBUG}", "-Wl,-rpath,", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_LIBRARIES_DEBUG}", "-l", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_LD_FLAGS_DEBUG}", "", installation_tag) + }); + + runtime_ld_flags_release = flatten({ + prefix("${SPICY_CONFIG_RUNTIME_LIBRARY_DIRS_RELEASE}", "-L", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_LIBRARY_DIRS_RELEASE}", "-Wl,-rpath,", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_LIBRARIES_RELEASE}", "-l", installation_tag), + prefix("${SPICY_CONFIG_RUNTIME_LD_FLAGS_RELEASE}", "", installation_tag) + }); +}; + +Configuration& spicy::configuration() { + static Configuration singleton; + return singleton; +} diff --git a/spicy/src/rt/base64.cc b/spicy/src/rt/base64.cc new file mode 100644 index 000000000..09ab72893 --- /dev/null +++ b/spicy/src/rt/base64.cc @@ -0,0 +1,98 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +extern "C" { +#include +#include +} + +using namespace spicy::rt; +using namespace spicy::rt::base64; + +struct detail::State { + base64_encodestate estate; + base64_decodestate dstate; +}; + +Stream::Stream() { + _state = std::shared_ptr(new detail::State(), [](auto p) { + // Nothing else to clean up. + delete p; // NOLINT(cppcoreguidelines-owning-memory) + }); + + base64_init_encodestate(&_state->estate); + base64_init_decodestate(&_state->dstate); +} + +// Don't finish the stream here, it might be shared with other instances. +// It'll eventually be cleaned up. +Stream::~Stream() = default; + +hilti::rt::Bytes Stream::encode(const hilti::rt::Bytes& data) { + if ( ! _state ) + throw Base64Error("encoding already finished"); + + char buf[static_cast(data.size() * 2)]; + int len = base64_encode_block(reinterpret_cast(data.data()), static_cast(data.size()), buf, + &_state->estate); + return hilti::rt::Bytes(buf, len); +} + +hilti::rt::Bytes Stream::encode(const hilti::rt::stream::View& data) { + if ( ! _state ) + throw Base64Error("encoding already finished"); + + hilti::rt::Bytes encoded; + + for ( auto block = data.firstBlock(); block; block = data.nextBlock(block) ) { + char buf[static_cast(block->size * 2)]; + int len = base64_encode_block(reinterpret_cast(block->start), static_cast(block->size), buf, + &_state->estate); + encoded.append(hilti::rt::Bytes(buf, len)); + } + + return encoded; +} + +hilti::rt::Bytes Stream::decode(const hilti::rt::Bytes& data) { + if ( ! _state ) + throw Base64Error("decoding already finished"); + + char buf[static_cast(data.size() * 2)]; + int len = base64_decode_block(reinterpret_cast(data.data()), static_cast(data.size()), buf, + &_state->dstate); + + return hilti::rt::Bytes(buf, len); +} + +hilti::rt::Bytes Stream::decode(const hilti::rt::stream::View& data) { + if ( ! _state ) + throw Base64Error("decoding already finished"); + + hilti::rt::Bytes decoded; + + for ( auto block = data.firstBlock(); block; block = data.nextBlock(block) ) { + char buf[static_cast(block->size * 2)]; + int len = base64_decode_block(reinterpret_cast(block->start), static_cast(block->size), buf, + &_state->dstate); + decoded.append(hilti::rt::Bytes(buf, len)); + } + + return decoded; +} + +hilti::rt::Bytes Stream::finish() { + // This can be safely called for both encoding and decoding, but won't do + // anything for the latter. + hilti::rt::Bytes b; + char buf[4]; + + // blockend always adds a trailing newline that we don't want. + int len = base64_encode_blockend(buf, &_state->estate); + b.append(hilti::rt::Bytes(buf, len - 1)); + + _state = nullptr; + return b; +} diff --git a/spicy/src/rt/driver.cc b/spicy/src/rt/driver.cc new file mode 100644 index 000000000..4b326c04a --- /dev/null +++ b/spicy/src/rt/driver.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +#include +#include + +#include + +using hilti::rt::Nothing; +using hilti::rt::Result; +using namespace hilti::rt::result; +using hilti::rt::fmt; + +using namespace spicy::rt; + +inline void Driver::_debug(const std::string_view& msg) { + if ( ! _enable_debug ) + return; + + HILTI_RT_DEBUG("spicy-driver", msg); +} + +void Driver::_debug_stats(const hilti::rt::ValueReference& data) { + if ( ! _enable_debug ) + return; + + auto pretty_print = [](uint64_t n) { + if ( n > 1024 * 1024 * 1024 ) + return fmt("%" PRIu64 "G", n / 1024 / 1024 / 1024); + if ( n > 1024 * 1024 ) + return fmt("%" PRIu64 "M", n / 1024 / 1024); + if ( n > 1024 ) + return fmt("%" PRIu64 "K", n / 1024); + return fmt("%" PRIu64, n); + }; + + auto data_begin = data->begin().offset(); + auto data_end = data_begin + data->size(); + auto data_chunks = pretty_print(data->numberChunks()); + auto data_size_cur = pretty_print(data->size()); + auto data_size_total = pretty_print(data_end); + + _debug(fmt("input : size-current=%s size-total=%s chunks-cur=%s offset-head=%" PRIu64 " offset-tail=%" PRIu64, + data_size_cur, data_size_total, data_chunks, data_begin, data_end)); + + auto stats = hilti::rt::memory_statistics(); + + auto memory_heap = pretty_print(stats.memory_heap); + auto num_stacks = pretty_print(stats.num_fibers); + auto max_stacks = pretty_print(stats.max_fibers); + auto cached_stacks = pretty_print(stats.cached_fibers); + + _debug(fmt("memory: heap=%s fibers-cur=%s fibers-cached=%s fibers-max=%s", memory_heap, num_stacks, cached_stacks, + max_stacks)); +} + +Result Driver::listParsers(std::ostream& out) { + if ( ! hilti::rt::isInitialized() ) + return Error("runtime not intialized"); + + const auto& parsers = spicy::rt::parsers(); + + if ( parsers.empty() ) { + out << "No parsers available.\n"; + return Nothing(); + } + + out << "Available parsers:\n\n"; + + for ( const auto& p : parsers ) { + std::string description; + std::string mime_types; + std::string ports; + + if ( p->description.size() ) + description = fmt(" %s", p->description); + + if ( p->mime_types.size() ) + mime_types = fmt(" %s", p->mime_types); + + if ( p->ports.size() ) + ports = fmt(" %s", p->ports); + + out << fmt(" %15s %s%s%s\n", p->name, description, ports, mime_types); + } + + out << "\n"; + return Nothing(); +} + +Result Driver::lookupParser(const std::string& parser_name) { + if ( ! hilti::rt::isInitialized() ) + return Error("runtime not intialized"); + + const auto& parsers = spicy::rt::parsers(); + + if ( parsers.empty() ) + return Error("no parsers available"); + + if ( parser_name.empty() ) { + if ( parsers.size() > 1 ) + return Error("multiple parsers available, need to select one"); + + return parsers.front(); + } + + else { + for ( const auto& i : parsers ) { + if ( i->name == parser_name ) + return i; + } + + return Error(fmt("spicy-driver: parser '%s' is not available", parser_name)); + } +} + +Result Driver::processInput(const spicy::rt::Parser& parser, std::istream& in, int increment) { + if ( ! hilti::rt::isInitialized() ) + return Error("runtime not intialized"); + + char buffer[4096]; + hilti::rt::ValueReference data; + std::optional r; + + _debug_stats(data); + + while ( in.good() && ! in.eof() ) { + auto len = (increment > 0 ? increment : sizeof(buffer)); + + in.read(buffer, len); + + if ( auto n = in.gcount() ) + data->append(hilti::rt::Bytes(buffer, n)); + + if ( in.peek() == EOF ) + data->freeze(); + + if ( ! r ) { + _debug(fmt("beginning parsing input (eod=%s)", data->isFrozen())); + r = parser.parse1(data, {}); + } + else { + _debug(fmt("resuming parsing input (eod=%s)", data->isFrozen())); + r->resume(); + } + + if ( *r ) { + _debug(fmt("finished parsing input (eod=%s)", data->isFrozen())); + _debug_stats(data); + break; + } + else { + _debug("parsing yielded"); + _debug_stats(data); + } + } + + return Nothing(); +} diff --git a/spicy/src/rt/global-state.cc b/spicy/src/rt/global-state.cc new file mode 100644 index 000000000..966663b05 --- /dev/null +++ b/spicy/src/rt/global-state.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace spicy::rt; +using namespace spicy::rt::detail; + +// Not memory-managed through smart pointer, need to control when we release it. +GlobalState* detail::__global_state = nullptr; + +GlobalState* detail::createGlobalState() { + __global_state = new GlobalState(); // NOLINT(cppcoreguidelines-owning-memory) + return __global_state; +} + +GlobalState::~GlobalState() { HILTI_RT_DEBUG("libspicy", "destroying global state"); } diff --git a/spicy/src/rt/init.cc b/spicy/src/rt/init.cc new file mode 100644 index 000000000..92445c888 --- /dev/null +++ b/spicy/src/rt/init.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include +#include + +using namespace spicy::rt; +using namespace spicy::rt::detail; + +void spicy::rt::init() { + if ( globalState()->runtime_is_initialized ) + return; + + if ( ! hilti::rt::isInitialized() ) + fatalError("hilti::rt::init() must be called before spicy::rt::init()"); + + HILTI_RT_DEBUG("libspicy", "initializing runtime"); + + for ( const auto& p : globalState()->parsers ) + HILTI_RT_DEBUG("libspicy", fmt("registered parser %s", p->name)); + + globalState()->runtime_is_initialized = true; +} + +void spicy::rt::done() { + if ( ! globalState() ) + return; + + HILTI_RT_DEBUG("libspicy", "shutting down runtime"); + + delete __global_state; // NOLINT(cppcoreguidelines-owning-memory) + __global_state = nullptr; +} + +bool spicy::rt::isInitialized() { return globalState()->runtime_is_initialized; } diff --git a/spicy/src/rt/parser.cc b/spicy/src/rt/parser.cc new file mode 100644 index 000000000..c28b2782a --- /dev/null +++ b/spicy/src/rt/parser.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include +#include + +#include +#include +#include + +using namespace spicy::rt; +using namespace spicy::rt::detail; + +void detail::printParserState(const std::string& unit_id, const hilti::rt::ValueReference& data, + const hilti::rt::stream::View& cur, int64_t lahead, + const hilti::rt::stream::SafeConstIterator& lahead_end, const std::string& literal_mode, + bool trim) { + auto str = [&](const hilti::rt::stream::SafeConstIterator& begin, const hilti::rt::stream::SafeConstIterator& end) { + auto i = begin + 10; + if ( i >= end ) + return std::make_pair(hilti::rt::stream::View(begin, end), ""); + + return std::make_pair(hilti::rt::stream::View(begin, i), "..."); + }; + + auto na = hilti::rt::Stream("n/a"); + hilti::rt::stream::View lah_data = na.view(); + std::string lah_str = "n/a"; + std::string lah_dots; + + auto [input_data, input_dots] = str(cur.safeBegin(), cur.safeEnd()); + + if ( lahead ) { + std::tie(lah_data, lah_dots) = str(cur.safeBegin(), lahead_end); + lah_str = hilti::rt::fmt("%" PRId32, lahead); + } + + auto msg = hilti::rt::fmt("- state: type=%s input=\"%s%s\" stream=%p offsets=%" PRId64 "/%" PRId64 "/%" PRId64 + " chunks=%d frozen=%s mode=%s trim=%s lah=%" PRId64 " lah_token=\"%s%s\"", + unit_id, input_data, input_dots, data.get(), data->begin().offset(), cur.begin().offset(), + data->end().offset(), data->numberChunks(), (data->isFrozen() ? "yes" : "no"), + literal_mode, (trim ? "yes" : "no"), lah_str, lah_data, lah_dots); + + SPICY_RT_DEBUG_VERBOSE(msg); +} + +void detail::waitForEod(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + hilti::rt::StrongReference filters) { + auto min = std::numeric_limits::max(); + + if ( ! cur.isOpenEnded() ) + min = cur.end().offset() - cur.begin().offset(); + + waitForInputOrEod(data, cur, min, std::move(filters)); +} + +void detail::waitForInput(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + uint64_t min, const std::string& error_msg, const std::string& location, + hilti::rt::StrongReference + filters) { // NOLINT(performance-unnecessary-value-param) + while ( min > cur.size() ) + if ( ! waitForInputOrEod(data, cur, filters) ) { + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("insufficent input at end of data for stream %p (which is not ok here)", data.get())); + throw ParseError(error_msg, location); + } +} + +bool detail::waitForInputOrEod(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + uint64_t min, + hilti::rt::StrongReference + filters) { // NOLINT(performance-unnecessary-value-param) + while ( min > cur.size() ) { + if ( ! waitForInputOrEod(data, cur, filters) ) + return false; + } + + return true; +} + +bool detail::waitForInputOrEod(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + const hilti::rt::StrongReference& filters) { + auto old = cur.size(); + auto new_ = cur.size(); + + while ( old == new_ ) { + if ( detail::haveEod(data, cur) ) + return false; + + SPICY_RT_DEBUG_VERBOSE(hilti::rt::fmt("suspending to wait for more input for stream %p, currently have %lu", + data.get(), cur.size())); + hilti::rt::detail::yield(); + + auto x = cur.safeEnd(); + x += 0; + + if ( filters ) { + SPICY_RT_DEBUG_VERBOSE("resuming filter execution"); + spicy::rt::filter::flush(filters); + } + + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("resuming after insufficient input, now have %lu for stream %p", cur.size(), data.get())); + new_ = cur.size(); + } + + return true; +} + +void detail::waitForInput(hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur, + const std::string& error_msg, const std::string& location, + const hilti::rt::StrongReference& filters) { + if ( ! waitForInputOrEod(data, cur, filters) ) { + SPICY_RT_DEBUG_VERBOSE( + hilti::rt::fmt("insufficent input at end of data for stream %p (which is not ok here)", data.get())); + throw ParseError(error_msg, location); + } +} + +bool detail::atEod(const hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur) { + return cur.size() == 0 && detail::haveEod(data, cur); +} + +bool detail::haveEod(const hilti::rt::ValueReference& data, const hilti::rt::stream::View& cur) { + return data->isFrozen() || cur.end().offset() < data->end().offset(); + // We've the reached end-of-data if either (1) the bytes object is frozen + // (then the input won't change anymore), or (2) our view is limited to + // something before the current end (then even appending more data to the + // input won't help). + if ( data->isFrozen() ) + return true; + + if ( cur.isOpenEnded() ) + return false; + + return cur.end().offset() <= data->end().offset(); +} diff --git a/spicy/src/rt/sink.cc b/spicy/src/rt/sink.cc new file mode 100644 index 000000000..d46573a12 --- /dev/null +++ b/spicy/src/rt/sink.cc @@ -0,0 +1,445 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +using namespace spicy::rt; +using namespace spicy::rt::detail; + +using hilti::rt::fmt; + +void Sink::_init() { + assert(_states.empty() && _units.empty()); // must have been release already + + _policy = sink::ReassemblerPolicy::First; + _auto_trim = true; + _size = 0; + _initial_seq = 0; + _cur_rseq = 0; + _last_reassem_rseq = 0; + _trim_rseq = 0; + _chunks.clear(); +} + +Sink::ChunkList::iterator Sink::_addAndCheck(std::optional data, uint64_t rseq, uint64_t rupper, + ChunkList::iterator c) { + assert(! _chunks.empty()); + + // Special check for the common case of appending to the end. + if ( rseq == _chunks.back().rupper ) { + _chunks.emplace_back(std::move(data), rseq, rupper); + return std::next(_chunks.end(), -1); + } + + // Find the first block that doesn't come completely before the new data. + for ( ; c != _chunks.end() && c->rupper <= rseq; c++ ) + ; + + if ( c == _chunks.end() ) { + // c is the last block, and it comes completely before the new block. + _chunks.emplace_back(std::move(data), rseq, rupper); + return std::next(_chunks.end(), -1); + } + + if ( rupper <= c->rseq ) + // The new block comes completely before c. + return _chunks.insert(c, Chunk(std::move(data), rseq, rupper)); + + ChunkList::iterator new_c; + + // The blocks overlap, complain & break up. + + if ( rseq < c->rseq ) { + // The new block has a prefix that comes before c. + uint64_t prefix_len = c->rseq - rseq; + + if ( data ) { + auto prefix = data->sub(safe_begin(*data) + prefix_len); + new_c = _chunks.insert(c, Chunk(std::move(prefix), rseq, rseq + prefix_len)); + data = data->sub(safe_begin(*data) + prefix_len, safe_end(*data)); + } + + rseq += prefix_len; + } + + else + new_c = c; + + auto overlap_start = rseq; + auto new_c_len = rupper - rseq; + auto c_len = (c->rupper - overlap_start); + auto overlap_len = (new_c_len < c_len ? new_c_len : c_len); + + hilti::rt::Bytes old_data; + hilti::rt::Bytes new_data; + + if ( c->data ) + old_data = c->data->sub(overlap_start - c->rseq, overlap_start - c->rseq + overlap_len); + + if ( data ) + new_data = data->sub(overlap_len); + + _reportOverlap(overlap_start, old_data, new_data); + + if ( data && overlap_len < new_c_len ) { + // Recurse to resolve remainder of the new data. + data = data->sub(safe_begin(*data) + overlap_len, safe_end(*data)); + rseq += overlap_len; + + if ( new_c == c ) + new_c = _addAndCheck(std::move(*data), rseq, rupper, c); + else + _addAndCheck(std::move(*data), rseq, rupper, c); + } + + return new_c; +} + +bool Sink::_deliver(std::optional data, uint64_t rseq, uint64_t rupper) { + if ( ! data ) { + // A gap. + SPICY_RT_DEBUG_VERBOSE(fmt("hit gap with sink %p at rseq %" PRIu64, this, rseq)); + + if ( _cur_rseq != rupper ) { + _reportGap(rseq, (rupper - rseq)); + _cur_rseq = rupper; + } + + return false; + } + + if ( data->size() == 0 ) + // Empty chunk, nothing to do. + return true; + + SPICY_RT_DEBUG_VERBOSE( + fmt("starting to deliver %" PRIu64 " bytes to sink %p at rseq %" PRIu64, data->size(), this, rseq)); + + if ( _filter ) { + if ( ! _filter_data ) { + // Initialize on first data. + _filter_data = FilterData(); + _filter_data->output = spicy::rt::filter::init(_filter, _filter_data->input, _filter_data->input->view()); + _filter_data->output_cur = (*_filter_data->output).view(); + } + + _filter_data->input->append(*data); + spicy::rt::filter::flush(_filter); + + data = _filter_data->output_cur.data(); + _filter_data->output_cur = _filter_data->output_cur.advance(data->size()); + + if ( data->size() == 0 ) + // Empty chunk coming out of filter, nothing to do. + return true; + } + + _size += data->size(); + + for ( auto s : _states ) { + if ( s->resumable ) + throw ParseError("more data after sink's unit has already completed parsing"); + + s->data->append(*data); + s->resumable.resume(); + } + + _cur_rseq = rupper; + _last_reassem_rseq = rupper; + + SPICY_RT_DEBUG_VERBOSE(fmt("done delivering to sink %p", this)); + return true; +} + +void Sink::_newData(std::optional data, uint64_t rseq, uint64_t len) { + if ( len == 0 ) + // Nothing to do. + return; + + // Fast-path: if it's right at the end of the input stream, we + // haven't anything buffered, and we do auto-trimming, just pass on. + if ( _auto_trim && _chunks.empty() && rseq == _cur_rseq ) { + _debugReassembler("fastpath new data", data, rseq, len); + _deliver(data, rseq, rseq + len); + return; + } + + _debugReassembler("buffering data", data, rseq, len); + + ChunkList::iterator c; + auto rupper_rseq = rseq + len; + + if ( rupper_rseq <= _trim_rseq ) + // Old data, don't do any work for it. + goto exit; + + if ( rseq < _trim_rseq ) { + // Partially old data, just keep the good stuff. + auto amount_old = _trim_rseq - rseq; + rseq += amount_old; + + if ( data ) + data = data->sub(safe_begin(*data) + amount_old, safe_end(*data)); + } + + if ( _chunks.empty() ) { + _chunks.emplace_back(std::move(data), rseq, rseq + len); + c = std::next(_chunks.end(), -1); + } + else + c = _addAndCheck(std::move(data), rseq, rupper_rseq, _chunks.begin()); + + // See if we have data in order now to deliver. + + if ( c->rseq > _last_reassem_rseq || c->rupper <= _last_reassem_rseq ) + goto exit; + + // We've filled a leading hole. Deliver as much as possible. + _debugReassemblerBuffer("buffer content"); + + _tryDeliver(c); + return; + +exit: + _debugReassemblerBuffer("buffer content"); +} + +void Sink::_skip(uint64_t rseq) { + SPICY_RT_DEBUG_VERBOSE(fmt("skipping sink %p to rseq %" PRIu64, this, rseq)); + + if ( _auto_trim ) + _trim(rseq); // will report undelivered + else + _reportUndeliveredUpTo(rseq); + + _cur_rseq = rseq; + _last_reassem_rseq = rseq; + + _reportSkipped(rseq); + _tryDeliver(_chunks.begin()); +} + +void Sink::_trim(uint64_t rseq) { + if ( rseq != UINT64_MAX ) { + SPICY_RT_DEBUG_VERBOSE(fmt("trimming sink %p to rseq %" PRIu64, this, rseq)); + } + else { + SPICY_RT_DEBUG_VERBOSE(fmt("trimming sink %p to EOD", this)); + } + + for ( auto c = _chunks.begin(); c != _chunks.end(); c = _chunks.erase(c) ) { + if ( c->rseq >= rseq ) + break; + + if ( c->data && _cur_rseq < c->rseq ) + _reportUndelivered(c->rseq, *c->data); + } + + _trim_rseq = rseq; +} + +void Sink::_tryDeliver(ChunkList::iterator c) { + // Note that a new block may include both some old stuff and some new + // stuff. _addAndCheckk() will have split the new stuff off into its own + // block(s), but in the following loop we have to take care not to + // deliver already-delivered data. + + for ( ; c != _chunks.end(); c++ ) { + if ( c->rseq == _last_reassem_rseq ) { + // New stuff. + _last_reassem_rseq += (c->rupper - c->rseq); + if ( ! _deliver(c->data, c->rseq, c->rupper) ) { + // Hit gap. + if ( _auto_trim ) + // We trim just up to the gap here, excluding the gap itself. + // This will prevent future data beyond the gap from being + // delivered until we explicitly skip over it. + _trim(c->rseq); + + break; + } + } + } + + if ( _auto_trim ) + _trim(_last_reassem_rseq); +} + +void Sink::_reportGap(uint64_t rseq, uint64_t len) const { + SPICY_RT_DEBUG_VERBOSE(fmt("reporting gap in sink %p at rseq %" PRIu64, this, rseq)); + + for ( size_t i = 0; i < _states.size(); i++ ) + (*_states[i]->parser->__hook_gap)(_units[i], _aseq(rseq), len); +} + +void Sink::_reportOverlap(uint64_t rseq, const hilti::rt::Bytes& old, const hilti::rt::Bytes& new_) const { + SPICY_RT_DEBUG_VERBOSE(fmt("reporting overlap in sink %p at rseq %" PRIu64, this, rseq)); + + for ( size_t i = 0; i < _states.size(); i++ ) + (*_states[i]->parser->__hook_overlap)(_units[i], _aseq(rseq), old, new_); +} + +void Sink::_reportSkipped(uint64_t rseq) const { + SPICY_RT_DEBUG_VERBOSE(fmt("reporting skipped in sink %p to rseq %" PRIu64, this, rseq)); + + for ( size_t i = 0; i < _states.size(); i++ ) + (*_states[i]->parser->__hook_skipped)(_units[i], _aseq(rseq)); +} + +void Sink::_reportUndelivered(uint64_t rseq, const hilti::rt::Bytes& data) const { + SPICY_RT_DEBUG_VERBOSE(fmt("reporting undelivered in sink %p at rseq %" PRIu64, this, rseq)); + + for ( size_t i = 0; i < _states.size(); i++ ) + (*_states[i]->parser->__hook_undelivered)(_units[i], _aseq(rseq), data); +} + +void Sink::_reportUndeliveredUpTo(uint64_t rupper) const { + for ( const auto& c : _chunks ) { + if ( c.rseq >= rupper ) + break; + + if ( ! c.data ) + continue; + + hilti::rt::Bytes b; + + if ( c.rupper <= rupper ) + b = *c.data; + + else + b = c.data->sub(c.rupper - rupper); + + _reportUndelivered(c.rseq, b); + } +} + +void Sink::_debugReassembler(const std::string& msg, const std::optional& data, uint64_t rseq, + uint64_t len) const { + if ( ! debug::wantVerbose() ) + return; + + if ( data ) { + auto escaped = hilti::rt::escapeBytes(data->str()); + if ( escaped.size() > 50 ) + escaped = escaped.substr(0, 50) + "..."; + + SPICY_RT_DEBUG_VERBOSE(fmt("reassembler/%p: %s rseq=% " PRIu64 " upper=%" PRIu64 " |%s| (%" PRIu64 " bytes)", + this, msg, rseq, rseq + len, escaped, data->size())); + } + else + SPICY_RT_DEBUG_VERBOSE( + fmt("reassembler/%p: %s rseq=% " PRIu64 " upper=%" PRIu64 " ", this, msg, rseq, rseq + len)); +} + +void Sink::_debugReassemblerBuffer(const std::string& msg) const { + if ( ! debug::wantVerbose() ) + return; + + if ( _chunks.empty() ) { + SPICY_RT_DEBUG_VERBOSE(fmt("reassembler/%p: no data buffered", this)); + return; + } + + SPICY_RT_DEBUG_VERBOSE( + fmt("reassembler/%p: %s: (" + "cur_rseq=%" PRIu64 " " + "last_reassem_rseq=%" PRIu64 " " + "trim_rseq=%" PRIu64 ")", + this, msg, _cur_rseq, _last_reassem_rseq, _trim_rseq)); + + for ( const auto& [i, c] : hilti::rt::enumerate(_chunks) ) + _debugReassembler(fmt(" * chunk %d:", i), c.data, c.rseq, (c.rupper - c.rseq)); +} + +void Sink::connect_mime_type(const MIMEType& mt) { + auto connect_matching = [&](auto mt) { + if ( const auto& x = detail::globalState()->parsers_by_mime_type.find(mt.asKey()); + x != detail::globalState()->parsers_by_mime_type.end() ) { + for ( const auto& p : x->second ) { + auto [unit, state] = (*p->__parse_sink)(); + + SPICY_RT_DEBUG_VERBOSE(fmt("connecting parser %s [%p] to sink %p for MIME type %s", p->name, &unit, + this, std::string(mt))); + _units.emplace_back(std::move(unit)); + _states.emplace_back(state); + } + } + }; + + connect_matching(mt); // NOLINT(clang-analyzer-core.uninitialized.Branch) I believe this is a FP -Robin + connect_matching(MIMEType(mt.mainType(), "*")); + connect_matching(MIMEType("*", "*")); +} + +void Sink::_close(bool orderly) { + spicy::rt::filter::disconnect(_filter); + _filter_data = {}; + + if ( _states.size() ) { + SPICY_RT_DEBUG_VERBOSE( + fmt("closing sink, disconnecting parsers from sink %p%s", this, (orderly ? "" : " (abort)"))); + + for ( auto s : _states ) { + if ( ! s->resumable ) { + s->data->freeze(); + if ( orderly ) + s->resumable.resume(); + else + s->resumable.abort(); + + assert(s->resumable); // must have conluded after freezing/aborting + } + + delete s; // NOLINT + } + + _states.clear(); + _units.clear(); + } + + _init(); +} + +void Sink::gap(uint64_t seq, uint64_t len) { _newData({}, _rseq(seq), len); } + +void Sink::skip(uint64_t seq) { + _skip(_rseq(seq)); + _debugReassemblerBuffer("buffer after skip"); +} + +void Sink::trim(uint64_t seq) { + _trim(_rseq(seq)); + _debugReassemblerBuffer("buffer after trim"); +} + +void Sink::write(hilti::rt::Bytes data, std::optional seq, std::optional len) { + if ( ! data.size() ) + return; + + uint64_t n; + + if ( len ) + n = *len; + else + n = data.size(); + + if ( seq ) + _newData(std::move(data), _rseq(*seq), n); + else + // Just append. + _newData(std::move(data), _cur_rseq, n); +} + +#if 0 +void Sink::write(const hilti::rt::Bytes& data, std::optional seq, std::optional len) { + if ( ! data.size() ) + return; + + for ( auto s : _states ) { + if ( s->resumable ) + throw ParseError("more data after sink's unit has already completed parsing"); + + s->data += data; + s->resumable.resume(); + } +} +#endif diff --git a/spicy/src/rt/util.cc b/spicy/src/rt/util.cc new file mode 100644 index 000000000..ef3d10bcb --- /dev/null +++ b/spicy/src/rt/util.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include + +#include +#include + +std::string spicy::rt::version() { + constexpr char spicy_version[] = PROJECT_VERSION_STRING_LONG; + +#if HILTI_RT_BUILD_TYPE_DEBUG + return hilti::rt::fmt("Spicy runtime library version %s [debug build]", spicy_version); +#elif HILTI_RT_BUILD_TYPE_RELEASE + return hilti::rt::fmt("Spicy runtime library version %s [release build]", spicy_version); +#else +#error "Neither HILTI_RT_BUILD_TYPE_DEBUG nor HILTI_RT_BUILD_TYPE_RELEASE define." +#endif +} diff --git a/spicy/src/rt/zlib.cc b/spicy/src/rt/zlib.cc new file mode 100644 index 000000000..beeb18fcd --- /dev/null +++ b/spicy/src/rt/zlib.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include + +using namespace spicy::rt; +using namespace spicy::rt::zlib; + +struct detail::State { + z_stream stream; +}; + +Stream::Stream() { + _state = std::shared_ptr(new detail::State(), [](auto p) { + inflateEnd(&p->stream); + delete p; // NOLINT(cppcoreguidelines-owning-memory) + }); + + // "15" here means maximum compression. "32" is a gross overload hack + // that means "check it for whether it's a gzip file". Sheesh. + if ( inflateInit2(&_state->stream, 15 + 32) != Z_OK ) { + _state = nullptr; + throw ZlibError("inflateInit2 failed"); + } +} + +// Don't finish the stream here, it might be shared with other instances. +Stream::~Stream() = default; + +hilti::rt::Bytes Stream::finish() { return hilti::rt::Bytes(); } + +hilti::rt::Bytes Stream::decompress(const hilti::rt::stream::View& data) { + if ( ! _state ) + // Not sure if this should throw an exception instead. However, an + // old comment indicated that this may be expected behaviour at least + // with the HTTP analyzer, so leaving it as returning just empty dasa + // for now. + return hilti::rt::Bytes(); + + hilti::rt::Bytes decoded; + + for ( auto block = data.firstBlock(); block; block = data.nextBlock(block) ) { + do { + char buf[4096]; + _state->stream.next_in = const_cast(block->start); + _state->stream.avail_in = block->size; + _state->stream.next_out = reinterpret_cast(buf); + _state->stream.avail_out = sizeof(buf); + int zip_status = inflate(&_state->stream, Z_SYNC_FLUSH); + + if ( zip_status != Z_STREAM_END && zip_status != Z_OK && zip_status != Z_BUF_ERROR ) { + _state = nullptr; + throw ZlibError("inflate failed"); + } + + if ( auto len = (sizeof(buf) - _state->stream.avail_out) ) + decoded.append(hilti::rt::Bytes(buf, static_cast(len))); + + if ( zip_status == Z_STREAM_END ) { + finish(); + break; + } + + } while ( _state->stream.avail_out == 0 ); + } + + return decoded; +} + +hilti::rt::Bytes Stream::decompress(const hilti::rt::Bytes& data) { + if ( ! _state ) + // Not sure if this should throw an exception instead. However, an + // old comment indicated that this may be expected behaviour at least + // with the HTTP analyzer, so leaving it as returning just empty dasa + // for now. + return hilti::rt::Bytes(); + + hilti::rt::Bytes decoded; + + do { + char buf[4096]; + _state->stream.next_in = const_cast(reinterpret_cast(data.data())); + _state->stream.avail_in = data.size(); + _state->stream.next_out = reinterpret_cast(buf); + _state->stream.avail_out = sizeof(buf); + int zip_status = inflate(&_state->stream, Z_SYNC_FLUSH); + + if ( zip_status != Z_STREAM_END && zip_status != Z_OK && zip_status != Z_BUF_ERROR ) { + _state = nullptr; + throw ZlibError("inflate failed"); + } + + if ( auto len = (sizeof(buf) - _state->stream.avail_out) ) + decoded.append(hilti::rt::Bytes(buf, static_cast(len))); + + if ( zip_status == Z_STREAM_END ) { + finish(); + break; + } + + } while ( _state->stream.avail_out == 0 ); + + return decoded; +} diff --git a/spicy/tests/grammar.cc b/spicy/tests/grammar.cc new file mode 100644 index 000000000..8d925d70e --- /dev/null +++ b/spicy/tests/grammar.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#include + +#include +#include +#include +#include +#include + +#include + +static auto literal(const std::string& symbol, std::string value) { + auto c = hilti::ctor::Bytes(std::move(value)); + return spicy::detail::codegen::production::Ctor(symbol, std::move(c)); +} + +static auto sequence(const std::string& symbol, std::vector l) { + return spicy::detail::codegen::production::Sequence(symbol, std::move(l)); +} + +static auto variable(const std::string& symbol, spicy::Type t) { + return spicy::detail::codegen::production::Variable(symbol, std::move(t)); +} + +static auto type_literal(const std::string& symbol, spicy::Type t) { + return spicy::detail::codegen::production::TypeLiteral(symbol, std::move(t)); +} + +static auto unresolved() { return spicy::detail::codegen::production::Unresolved(); } + +static auto lookAhead(const std::string& symbol, spicy::detail::codegen::Production a1, + spicy::detail::codegen::Production a2) { + return spicy::detail::codegen::production::LookAhead(symbol, std::move(a1), std::move(a2), + spicy::detail::codegen::production::look_ahead::Default::None); +} + +static auto epsilon() { return spicy::detail::codegen::production::Epsilon(); } + +static hilti::Result finalize(spicy::detail::codegen::Grammar* g, + const spicy::detail::codegen::Production& root) { + if ( auto result = g->setRoot(root); ! result ) + return result; + + if ( auto result = g->finalize(); ! result ) + return result; + + return hilti::Nothing(); +} + +TEST_CASE("basic") { + auto g = spicy::detail::codegen::Grammar("basic"); + auto l1 = literal("l1", "l1-val"); + auto l2 = literal("l2", "l2-val"); + auto l3 = literal("l3", "l3-val"); + auto r = sequence("S", {l1, l2, l3}); + CHECK(finalize(&g, r)); +} + +TEST_CASE("example1") { + // Simple example grammar from + // + // http://www.cs.uky.edu/~lewis/essays/compilers/td-parse.html + // + // Ambiguity expected. + + auto g = spicy::detail::codegen::Grammar("example1"); + + auto a = literal("a", "a"); + auto b = literal("b", "b"); + auto c = literal("c", "c"); + auto d = literal("d", "d"); + + auto A = unresolved(); + auto C = unresolved(); + auto D = unresolved(); + + auto cC = sequence("cC", {c, C}); + auto bD = sequence("bD", {b, D}); + auto AD = sequence("AD", {A, D}); + auto aA = sequence("aA", {a, A}); + + g.resolve(&A, lookAhead("A", epsilon(), a)); + auto B = lookAhead("B", epsilon(), bD); + g.resolve(&C, lookAhead("C", AD, b)); + g.resolve(&D, lookAhead("D", aA, c)); + + auto ABA = sequence("ABA", {A, B, A}); + auto S = lookAhead("S", ABA, cC); + + CHECK(finalize(&g, S) == + hilti::Result(hilti::result::Error( + "grammar example1, production A is ambigious for look-ahead symbol(s) { b\"a\" (bytes) }"))); +} + +TEST_CASE("example2") { + // Simple example grammar from "Parsing Techniques", Fig. 8.9 + + auto g = spicy::detail::codegen::Grammar("example2"); + + auto hs = literal("hs", "#"); + auto pl = literal("pl", "("); + auto pr = literal("pr", ")"); + auto no = literal("no", "!"); + auto qu = literal("qu", "?"); + auto st = variable("st", spicy::type::Bytes()); + + auto FsQ = unresolved(); + auto SS = unresolved(); + auto FFs = unresolved(); + + auto F = sequence("Fact", {no, st}); + auto Q = sequence("Question", {qu, st}); + auto S = lookAhead("Session", FsQ, SS); + g.resolve(&SS, sequence("SS", {pl, S, pr, S})); + auto Fs = lookAhead("Facts", FFs, epsilon()); + g.resolve(&FsQ, sequence("FsQ", {Fs, Q})); + g.resolve(&FFs, sequence("FFs", {F, Fs})); + auto root = sequence("Start", {S, hs}); + CHECK(finalize(&g, root)); +} + +TEST_CASE("example3") { + auto g = spicy::detail::codegen::Grammar("example3"); + + auto hdrkey = ::type_literal("HdrKey", spicy::type::Bytes()); + auto hdrval = ::type_literal("HdrVal", spicy::type::Bytes()); + auto colon = literal("colon", ":"); + auto ws = literal("ws", "[ \t]*"); + auto nl = literal("nl", "[\r\n]"); + auto header = sequence("Header", {hdrkey, ws, colon, ws, hdrval, nl}); + auto list1 = unresolved(); + auto list2 = lookAhead("List2", list1, epsilon()); + g.resolve(&list1, sequence("List1", {header, list2})); + auto all = lookAhead("All", list2, nl); + CHECK(finalize(&g, all)); +} + +TEST_CASE("example4") { + auto g = spicy::detail::codegen::Grammar("example4"); + + auto hdrkey = literal("hk", "hv"); + auto hdrval = literal("hv", "hk"); + auto colon = literal("colon", ":"); + auto ws = literal("ws", "[ \t]*"); + auto nl = literal("nl", "[\r\n]"); + // auto header = sequence("Header", {hdrkey, ws, colon, ws, hdrval, nl}); + auto all = unresolved(); + auto list1 = unresolved(); + auto list2 = lookAhead("List2", ws, epsilon()); + g.resolve(&list1, sequence("List1", {list2})); + g.resolve(&all, sequence("All", {list2, colon})); + CHECK(finalize(&g, all)); +} diff --git a/spicy/tests/main.cc b/spicy/tests/main.cc new file mode 100644 index 000000000..ce23c64db --- /dev/null +++ b/spicy/tests/main.cc @@ -0,0 +1,4 @@ +// Copyright (c) 2020 by the Zeek Project. See LICENSE for details. + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 000000000..8de60ac6b --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,3 @@ +diag.log +.tmp +.btest.failed.dat diff --git a/tests/Baseline/hilti.ast.basic-module/output b/tests/Baseline/hilti.ast.basic-module/output new file mode 100644 index 000000000..2410a030e --- /dev/null +++ b/tests/Baseline/hilti.ast.basic-module/output @@ -0,0 +1,68 @@ +[debug/compiler] parsing file "/home/robin/work/spicy/tests/.tmp/hilti.ast.basic-module/basic-module.hlt" +[debug/compiler] registering AST for module Foo ("/home/robin/work/spicy/tests/.tmp/hilti.ast.basic-module/basic-module.hlt") +[debug/compiler] processing AST, round 1 +[debug/compiler] performing missing imports for module Foo +[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: (none)) +[debug/compiler] modules: Foo +[debug/compiler] resetting nodes for module Foo +[debug/compiler] building scopes for all module modules +[debug/compiler] resolving IDs in module Foo +[debug/compiler] -> modified +[debug/compiler] resolving operators in module Foo +[debug/compiler] coercing expressions for Foo +[debug/compiler] processing AST, round 2 +[debug/compiler] performing missing imports for module Foo +[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: (none)) +[debug/compiler] modules: Foo +[debug/compiler] resetting nodes for module Foo +[debug/compiler] building scopes for all module modules +[debug/compiler] resolving IDs in module Foo +[debug/compiler] resolving operators in module Foo +[debug/compiler] coercing expressions for Foo +[debug/ast-final] # Foo: Final AST +[debug/ast-final] - Module %1 (basic-module.hlt:5-11) +[debug/ast-final] | Foo -> Module %1 +[debug/ast-final] | X -> declaration::Type %3 +[debug/ast-final] | foo -> declaration::Function %4 +[debug/ast-final] - ID (basic-module.hlt:5) +[debug/ast-final] - statement::Block (basic-module.hlt:5-11) +[debug/ast-final] - declaration::Type %3 (basic-module.hlt:5-7) +[debug/ast-final] - ID (basic-module.hlt:7) +[debug/ast-final] - type::Bool (basic-module.hlt:7) (non-const) (type-id: Foo::X) +[debug/ast-final] - node::None (basic-module.hlt:5-7) +[debug/ast-final] - declaration::Function %4 (basic-module.hlt:9) +[debug/ast-final] - Function > (basic-module.hlt:9) +[debug/ast-final] | bar -> declaration::Parameter %2 +[debug/ast-final] - ID (basic-module.hlt:9) +[debug/ast-final] - type::Function (basic-module.hlt:9) (non-const) +[debug/ast-final] - type::function::Result (basic-module.hlt:9) +[debug/ast-final] - type::String (basic-module.hlt:9) (non-const) +[debug/ast-final] - declaration::Parameter %2 (basic-module.hlt:9) +[debug/ast-final] - ID (basic-module.hlt:9) +[debug/ast-final] - type::Real (basic-module.hlt:9) (non-const) +[debug/ast-final] - node::None (basic-module.hlt:9) +[debug/ast-final] - node::None (basic-module.hlt:9) +[debug/ast-final] - node::None (basic-module.hlt:5-7) +[debug/compiler] validating module Foo (post-transform) +[debug/compiler] updated cached AST for module Foo (final: yes, requires_compilation: yes, dependencies: (none)) +[debug/compiler] compiling module Foo to C++ +[debug/compiler] finalizing module Foo +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/hilti.ast.basic-module/basic-module.hlt") +// Compiled by HILTI version 0.3.0-branch + +#include + +#include + +namespace __hlt::Foo { + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.ast.basic-module/basic-module.hlt","version":1} +*/ + diff --git a/tests/Baseline/hilti.ast.coercion-type-changes/output b/tests/Baseline/hilti.ast.coercion-type-changes/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.ast.coercion/output b/tests/Baseline/hilti.ast.coercion/output new file mode 100644 index 000000000..eb7029bf2 --- /dev/null +++ b/tests/Baseline/hilti.ast.coercion/output @@ -0,0 +1,142 @@ +[debug/ast-final] # Foo: Final AST +[debug/ast-final] - Module %1 (coercion.hlt:5-31) +[debug/ast-final] | A -> declaration::GlobalVariable %2 +[debug/ast-final] | B -> declaration::GlobalVariable %3 +[debug/ast-final] | C -> declaration::GlobalVariable %4 +[debug/ast-final] | D -> declaration::GlobalVariable %5 +[debug/ast-final] | E -> declaration::GlobalVariable %10 +[debug/ast-final] | Foo -> Module %1 +[debug/ast-final] | x -> declaration::Function %6 +[debug/ast-final] | y -> declaration::Function %7 +[debug/ast-final] | z -> declaration::Function %8 +[debug/ast-final] | z2 -> declaration::Function %9 +[debug/ast-final] - ID (coercion.hlt:5) +[debug/ast-final] - statement::Block (coercion.hlt:5-31) +[debug/ast-final] - declaration::GlobalVariable %2 (coercion.hlt:5-7) +[debug/ast-final] - ID (coercion.hlt:7) +[debug/ast-final] - type::Real (coercion.hlt:7) (non-const) +[debug/ast-final] - expression::Ctor (coercion.hlt:7) +[debug/ast-final] - ctor::Coerced (coercion.hlt:7) +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:7) +[debug/ast-final] - ctor::Real (coercion.hlt:7) +[debug/ast-final] - declaration::GlobalVariable %3 (coercion.hlt:7-8) +[debug/ast-final] - ID (coercion.hlt:8) +[debug/ast-final] - type::Real (coercion.hlt:8) (non-const) +[debug/ast-final] - expression::Ctor (coercion.hlt:8) +[debug/ast-final] - ctor::Coerced (coercion.hlt:8) +[debug/ast-final] - ctor::SignedInteger (coercion.hlt:8) +[debug/ast-final] - ctor::Real (coercion.hlt:8) +[debug/ast-final] - declaration::GlobalVariable %4 (coercion.hlt:8-9) +[debug/ast-final] - ID (coercion.hlt:9) +[debug/ast-final] - type::Stream (coercion.hlt:9) (non-const) +[debug/ast-final] - expression::Coerced (coercion.hlt:9) +[debug/ast-final] - expression::Ctor (coercion.hlt:9) +[debug/ast-final] - ctor::Bytes (coercion.hlt:9) +[debug/ast-final] - type::Stream (coercion.hlt:9) (non-const) +[debug/ast-final] - declaration::GlobalVariable %5 (coercion.hlt:9-10) +[debug/ast-final] - ID (coercion.hlt:10) +[debug/ast-final] - type::String (coercion.hlt:10) (non-const) +[debug/ast-final] - expression::Ctor (coercion.hlt:10) +[debug/ast-final] - ctor::String (coercion.hlt:10) +[debug/ast-final] - declaration::Function %6 (coercion.hlt:10-14) +[debug/ast-final] - Function > (coercion.hlt:12-14) +[debug/ast-final] - ID (coercion.hlt:12) +[debug/ast-final] - type::Function (coercion.hlt:12-14) (non-const) +[debug/ast-final] - type::function::Result (coercion.hlt:12) +[debug/ast-final] - type::Bool (coercion.hlt:12) (non-const) +[debug/ast-final] - statement::Block (coercion.hlt:12-14) +[debug/ast-final] - statement::Return (coercion.hlt:13) +[debug/ast-final] - expression::Ctor (coercion.hlt:13) +[debug/ast-final] - ctor::Bool (coercion.hlt:13) +[debug/ast-final] - node::None (coercion.hlt:12-14) +[debug/ast-final] - declaration::Function %7 (coercion.hlt:14-18) +[debug/ast-final] - Function > (coercion.hlt:16-18) +[debug/ast-final] - ID (coercion.hlt:16) +[debug/ast-final] - type::Function (coercion.hlt:16-18) (non-const) +[debug/ast-final] - type::function::Result (coercion.hlt:16) +[debug/ast-final] - type::Real (coercion.hlt:16) (non-const) +[debug/ast-final] - statement::Block (coercion.hlt:16-18) +[debug/ast-final] - statement::Return (coercion.hlt:17) +[debug/ast-final] - expression::Ctor (coercion.hlt:17) +[debug/ast-final] - ctor::Coerced (coercion.hlt:17) +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:17) +[debug/ast-final] - ctor::Real (coercion.hlt:17) +[debug/ast-final] - node::None (coercion.hlt:12-14) +[debug/ast-final] - declaration::Function %8 (coercion.hlt:18-22) +[debug/ast-final] - Function > (coercion.hlt:20-22) +[debug/ast-final] - ID (coercion.hlt:20) +[debug/ast-final] - type::Function (coercion.hlt:20-22) (non-const) +[debug/ast-final] - type::function::Result (coercion.hlt:20) +[debug/ast-final] - type::stream::View (coercion.hlt:20) (non-const) +[debug/ast-final] - type::Stream (coercion.hlt:20) +[debug/ast-final] - statement::Block (coercion.hlt:20-22) +[debug/ast-final] - statement::Return (coercion.hlt:21) +[debug/ast-final] - expression::Coerced (coercion.hlt:21) +[debug/ast-final] - expression::ResolvedID (type: stream) (coercion.hlt:21) +[debug/ast-final] - ID (coercion.hlt:21) +[debug/ast-final] - type::stream::View (coercion.hlt:20) (non-const) +[debug/ast-final] - type::Stream (coercion.hlt:20) +[debug/ast-final] - node::None (coercion.hlt:12-14) +[debug/ast-final] - declaration::Function %9 (coercion.hlt:22-26) +[debug/ast-final] - Function > (coercion.hlt:24-26) +[debug/ast-final] - ID (coercion.hlt:24) +[debug/ast-final] - type::Function (coercion.hlt:24-26) (non-const) +[debug/ast-final] - type::function::Result (coercion.hlt:24) +[debug/ast-final] - type::Void (coercion.hlt:24) (non-const) +[debug/ast-final] - statement::Block (coercion.hlt:24-26) +[debug/ast-final] - statement::Return (coercion.hlt:25) +[debug/ast-final] - node::None (coercion.hlt:12-14) +[debug/ast-final] - declaration::GlobalVariable %10 (coercion.hlt:26-28) +[debug/ast-final] - ID (coercion.hlt:28) +[debug/ast-final] - type::Tuple (coercion.hlt:28) (non-const) +[debug/ast-final] - ID (coercion.hlt:28) +[debug/ast-final] - type::Real (coercion.hlt:28) (non-const) +[debug/ast-final] - ID (coercion.hlt:28) +[debug/ast-final] - type::SignedInteger (coercion.hlt:28) (non-const) +[debug/ast-final] - ID (coercion.hlt:28) +[debug/ast-final] - type::String (coercion.hlt:28) (non-const) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::Coerced (coercion.hlt:28) +[debug/ast-final] - ctor::Tuple (coercion.hlt:28) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::String (coercion.hlt:28) +[debug/ast-final] - ctor::Tuple (coercion.hlt:28) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::Coerced (coercion.hlt:28) +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28) +[debug/ast-final] - ctor::Real (coercion.hlt:28) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::Coerced (coercion.hlt:28) +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28) +[debug/ast-final] - ctor::SignedInteger (coercion.hlt:28) +[debug/ast-final] - expression::Ctor (coercion.hlt:28) +[debug/ast-final] - ctor::String (coercion.hlt:28) +module Foo { + +global real A = 3; +global real B = -5; +global stream C = b"X"; +global string D = "42"; +global tuple, string> E = (1, 2, "xyz"); + +function bool x() { + return True; +} + +function real y() { + return 1; +} + +function view z() { + return C; +} + +function void z2() { + return; +} + +} diff --git a/tests/Baseline/hilti.ast.import-from/output b/tests/Baseline/hilti.ast.import-from/output new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/tests/Baseline/hilti.ast.import-from/output @@ -0,0 +1 @@ +foo diff --git a/tests/Baseline/hilti.ast.imported-id/output b/tests/Baseline/hilti.ast.imported-id/output new file mode 100644 index 000000000..af44c62f3 --- /dev/null +++ b/tests/Baseline/hilti.ast.imported-id/output @@ -0,0 +1,399 @@ +[debug/compiler] parsing file "foo.hlt" +[debug/compiler] registering AST for module Foo ("/home/robin/work/spicy/tests/.tmp/hilti.ast.imported-id/foo.hlt") +[debug/compiler] processing AST, round 1 +[debug/compiler] performing missing imports for module Foo +[debug/compiler] parsing file "./bar.hlt" +[debug/compiler] loaded module Bar from "./bar.hlt" +[debug/compiler] registering AST for module Bar ("/home/robin/work/spicy/tests/.tmp/hilti.ast.imported-id/bar.hlt") +[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) +[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: Bar) +[debug/compiler] performing missing imports for module Bar +[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) +[debug/compiler] modules: Bar, Foo +[debug/compiler] resetting nodes for module Bar +[debug/compiler] resetting nodes for module Foo +[debug/compiler] building scopes for all module modules +[debug/ast-scopes] # Bar: AST with scopes (round 1) +[debug/ast-scopes] - Module %2 (bar.hlt:2-11) +[debug/ast-scopes] | Bar -> Module %2 +[debug/ast-scopes] | Bar1 -> declaration::Type %8 +[debug/ast-scopes] | Bar2 -> declaration::Type %9 +[debug/ast-scopes] | Foo -> declaration::ImportedModule %7 +[debug/ast-scopes] | bar -> declaration::Function %10 +[debug/ast-scopes] - ID (bar.hlt:2) +[debug/ast-scopes] - statement::Block (bar.hlt:2-11) +[debug/ast-scopes] - declaration::ImportedModule %7 (bar.hlt:4) +[debug/ast-scopes] | Bar -> declaration::ImportedModule %11 +[debug/ast-scopes] | Foo -> Module %1 +[debug/ast-scopes] | Foo1 -> declaration::Type %12 +[debug/ast-scopes] | Foo2 -> declaration::Type %13 +[debug/ast-scopes] | foo -> declaration::Function %14 +[debug/ast-scopes] - ID (bar.hlt:4) +[debug/ast-scopes] - declaration::Type %8 (bar.hlt:6) +[debug/ast-scopes] - ID (bar.hlt:6) +[debug/ast-scopes] - type::String (bar.hlt:6) (non-const) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Type %9 (bar.hlt:7) +[debug/ast-scopes] - ID (bar.hlt:7) +[debug/ast-scopes] - type::UnresolvedID (bar.hlt:7) (non-const) +[debug/ast-scopes] - ID (bar.hlt:7) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Function %10 (bar.hlt:9) +[debug/ast-scopes] - Function > (bar.hlt:9) +[debug/ast-scopes] | bar -> declaration::Parameter %3 +[debug/ast-scopes] | foo -> declaration::Parameter %4 +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::Function (bar.hlt:9) (non-const) +[debug/ast-scopes] - type::function::Result (bar.hlt:9) +[debug/ast-scopes] - type::String (bar.hlt:9) (non-const) +[debug/ast-scopes] - declaration::Parameter %3 (bar.hlt:9) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::UnresolvedID (bar.hlt:9) (non-const) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - declaration::Parameter %4 (bar.hlt:9) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::UnresolvedID (bar.hlt:9) (non-const) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] # Foo: AST with scopes (round 1) +[debug/ast-scopes] - Module %1 (foo.hlt:2-11) +[debug/ast-scopes] | Bar -> declaration::ImportedModule %11 +[debug/ast-scopes] | Foo -> Module %1 +[debug/ast-scopes] | Foo1 -> declaration::Type %12 +[debug/ast-scopes] | Foo2 -> declaration::Type %13 +[debug/ast-scopes] | foo -> declaration::Function %14 +[debug/ast-scopes] - ID (foo.hlt:2) +[debug/ast-scopes] - statement::Block (foo.hlt:2-11) +[debug/ast-scopes] - declaration::ImportedModule %11 (foo.hlt:4) +[debug/ast-scopes] | Bar -> Module %2 +[debug/ast-scopes] | Bar1 -> declaration::Type %8 +[debug/ast-scopes] | Bar2 -> declaration::Type %9 +[debug/ast-scopes] | Foo -> declaration::ImportedModule %7 +[debug/ast-scopes] | bar -> declaration::Function %10 +[debug/ast-scopes] - ID (foo.hlt:4) +[debug/ast-scopes] - declaration::Type %12 (foo.hlt:6) +[debug/ast-scopes] - ID (foo.hlt:6) +[debug/ast-scopes] - type::Bool (foo.hlt:6) (non-const) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Type %13 (foo.hlt:7) +[debug/ast-scopes] - ID (foo.hlt:7) +[debug/ast-scopes] - type::UnresolvedID (foo.hlt:7) (non-const) +[debug/ast-scopes] - ID (foo.hlt:7) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Function %14 (foo.hlt:9) +[debug/ast-scopes] - Function > (foo.hlt:9) +[debug/ast-scopes] | bar -> declaration::Parameter %6 +[debug/ast-scopes] | foo -> declaration::Parameter %5 +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::Function (foo.hlt:9) (non-const) +[debug/ast-scopes] - type::function::Result (foo.hlt:9) +[debug/ast-scopes] - type::String (foo.hlt:9) (non-const) +[debug/ast-scopes] - declaration::Parameter %5 (foo.hlt:9) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::UnresolvedID (foo.hlt:9) (non-const) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - declaration::Parameter %6 (foo.hlt:9) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::UnresolvedID (foo.hlt:9) (non-const) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/compiler] resolving IDs in module Bar +[debug/resolver] resolved ID Foo::Foo1 (./bar.hlt:7) to declaration::Type %12 (foo.hlt:6) +[debug/resolver] resolved ID Bar1 (./bar.hlt:9) to declaration::Type %8 (bar.hlt:6) +[debug/resolver] resolved ID Foo::Foo1 (./bar.hlt:9) to declaration::Type %12 (foo.hlt:6) +[debug/compiler] -> modified +[debug/compiler] resolving IDs in module Foo +[debug/resolver] resolved ID Bar::Bar1 (foo.hlt:7) to declaration::Type %8 (bar.hlt:6) +[debug/resolver] resolved ID Foo1 (foo.hlt:9) to declaration::Type %12 (foo.hlt:6) +[debug/resolver] resolved ID Bar::Bar1 (foo.hlt:9) to declaration::Type %8 (bar.hlt:6) +[debug/compiler] -> modified +[debug/compiler] resolving operators in module Bar +[debug/compiler] resolving operators in module Foo +[debug/compiler] coercing expressions for Bar +[debug/compiler] coercing expressions for Foo +[debug/compiler] processing AST, round 2 +[debug/compiler] performing missing imports for module Bar +[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) +[debug/compiler] performing missing imports for module Foo +[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: Bar) +[debug/compiler] modules: Bar, Foo +[debug/compiler] resetting nodes for module Bar +[debug/compiler] resetting nodes for module Foo +[debug/compiler] building scopes for all module modules +[debug/ast-scopes] # Bar: AST with scopes (round 2) +[debug/ast-scopes] - Module %2 (bar.hlt:2-11) +[debug/ast-scopes] | Bar -> Module %2 +[debug/ast-scopes] | Bar1 -> declaration::Type %8 +[debug/ast-scopes] | Bar2 -> declaration::Type %9 +[debug/ast-scopes] | Foo -> declaration::ImportedModule %7 +[debug/ast-scopes] | bar -> declaration::Function %10 +[debug/ast-scopes] - ID (bar.hlt:2) +[debug/ast-scopes] - statement::Block (bar.hlt:2-11) +[debug/ast-scopes] - declaration::ImportedModule %7 (bar.hlt:4) +[debug/ast-scopes] | Bar -> declaration::ImportedModule %11 +[debug/ast-scopes] | Foo -> Module %1 +[debug/ast-scopes] | Foo1 -> declaration::Type %12 +[debug/ast-scopes] | Foo2 -> declaration::Type %13 +[debug/ast-scopes] | foo -> declaration::Function %14 +[debug/ast-scopes] - ID (bar.hlt:4) +[debug/ast-scopes] - declaration::Type %8 (bar.hlt:6) +[debug/ast-scopes] - ID (bar.hlt:6) +[debug/ast-scopes] - type::String (bar.hlt:6) (non-const) (type-id: Bar::Bar1) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Type %9 (bar.hlt:7) +[debug/ast-scopes] - ID (bar.hlt:7) +[debug/ast-scopes] - type::ResolvedID (type: bool) (bar.hlt:7) (non-const) +[debug/ast-scopes] - ID (bar.hlt:7) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Function %10 (bar.hlt:9) +[debug/ast-scopes] - Function > (bar.hlt:9) +[debug/ast-scopes] | bar -> declaration::Parameter %3 +[debug/ast-scopes] | foo -> declaration::Parameter %4 +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::Function (bar.hlt:9) (non-const) +[debug/ast-scopes] - type::function::Result (bar.hlt:9) +[debug/ast-scopes] - type::String (bar.hlt:9) (non-const) +[debug/ast-scopes] - declaration::Parameter %3 (bar.hlt:9) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: string) (bar.hlt:9) (non-const) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - declaration::Parameter %4 (bar.hlt:9) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: bool) (bar.hlt:9) (non-const) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] # Foo: AST with scopes (round 2) +[debug/ast-scopes] - Module %1 (foo.hlt:2-11) +[debug/ast-scopes] | Bar -> declaration::ImportedModule %11 +[debug/ast-scopes] | Foo -> Module %1 +[debug/ast-scopes] | Foo1 -> declaration::Type %12 +[debug/ast-scopes] | Foo2 -> declaration::Type %13 +[debug/ast-scopes] | foo -> declaration::Function %14 +[debug/ast-scopes] - ID (foo.hlt:2) +[debug/ast-scopes] - statement::Block (foo.hlt:2-11) +[debug/ast-scopes] - declaration::ImportedModule %11 (foo.hlt:4) +[debug/ast-scopes] | Bar -> Module %2 +[debug/ast-scopes] | Bar1 -> declaration::Type %8 +[debug/ast-scopes] | Bar2 -> declaration::Type %9 +[debug/ast-scopes] | Foo -> declaration::ImportedModule %7 +[debug/ast-scopes] | bar -> declaration::Function %10 +[debug/ast-scopes] - ID (foo.hlt:4) +[debug/ast-scopes] - declaration::Type %12 (foo.hlt:6) +[debug/ast-scopes] - ID (foo.hlt:6) +[debug/ast-scopes] - type::Bool (foo.hlt:6) (non-const) (type-id: Foo::Foo1) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Type %13 (foo.hlt:7) +[debug/ast-scopes] - ID (foo.hlt:7) +[debug/ast-scopes] - type::ResolvedID (type: string) (foo.hlt:7) (non-const) +[debug/ast-scopes] - ID (foo.hlt:7) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Function %14 (foo.hlt:9) +[debug/ast-scopes] - Function > (foo.hlt:9) +[debug/ast-scopes] | bar -> declaration::Parameter %6 +[debug/ast-scopes] | foo -> declaration::Parameter %5 +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::Function (foo.hlt:9) (non-const) +[debug/ast-scopes] - type::function::Result (foo.hlt:9) +[debug/ast-scopes] - type::String (foo.hlt:9) (non-const) +[debug/ast-scopes] - declaration::Parameter %5 (foo.hlt:9) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: bool) (foo.hlt:9) (non-const) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - declaration::Parameter %6 (foo.hlt:9) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: string) (foo.hlt:9) (non-const) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/compiler] resolving IDs in module Bar +[debug/compiler] -> modified +[debug/compiler] resolving IDs in module Foo +[debug/compiler] -> modified +[debug/compiler] resolving operators in module Bar +[debug/compiler] resolving operators in module Foo +[debug/compiler] coercing expressions for Bar +[debug/compiler] coercing expressions for Foo +[debug/compiler] processing AST, round 3 +[debug/compiler] performing missing imports for module Bar +[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) +[debug/compiler] performing missing imports for module Foo +[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: Bar) +[debug/compiler] modules: Bar, Foo +[debug/compiler] resetting nodes for module Bar +[debug/compiler] resetting nodes for module Foo +[debug/compiler] building scopes for all module modules +[debug/ast-scopes] # Bar: AST with scopes (round 3) +[debug/ast-scopes] - Module %2 (bar.hlt:2-11) +[debug/ast-scopes] | Bar -> Module %2 +[debug/ast-scopes] | Bar1 -> declaration::Type %8 +[debug/ast-scopes] | Bar2 -> declaration::Type %9 +[debug/ast-scopes] | Foo -> declaration::ImportedModule %7 +[debug/ast-scopes] | bar -> declaration::Function %10 +[debug/ast-scopes] - ID (bar.hlt:2) +[debug/ast-scopes] - statement::Block (bar.hlt:2-11) +[debug/ast-scopes] - declaration::ImportedModule %7 (bar.hlt:4) +[debug/ast-scopes] | Bar -> declaration::ImportedModule %11 +[debug/ast-scopes] | Foo -> Module %1 +[debug/ast-scopes] | Foo1 -> declaration::Type %12 +[debug/ast-scopes] | Foo2 -> declaration::Type %13 +[debug/ast-scopes] | foo -> declaration::Function %14 +[debug/ast-scopes] - ID (bar.hlt:4) +[debug/ast-scopes] - declaration::Type %8 (bar.hlt:6) +[debug/ast-scopes] - ID (bar.hlt:6) +[debug/ast-scopes] - type::String (bar.hlt:6) (non-const) (type-id: Bar::Bar1) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Type %9 (bar.hlt:7) +[debug/ast-scopes] - ID (bar.hlt:7) +[debug/ast-scopes] - type::Bool (foo.hlt:6) (non-const) (type-id: Bar::Bar2) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Function %10 (bar.hlt:9) +[debug/ast-scopes] - Function > (bar.hlt:9) +[debug/ast-scopes] | bar -> declaration::Parameter %3 +[debug/ast-scopes] | foo -> declaration::Parameter %4 +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::Function (bar.hlt:9) (non-const) +[debug/ast-scopes] - type::function::Result (bar.hlt:9) +[debug/ast-scopes] - type::String (bar.hlt:9) (non-const) +[debug/ast-scopes] - declaration::Parameter %3 (bar.hlt:9) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: string) (bar.hlt:9) (non-const) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - declaration::Parameter %4 (bar.hlt:9) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: bool) (bar.hlt:9) (non-const) +[debug/ast-scopes] - ID (bar.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] # Foo: AST with scopes (round 3) +[debug/ast-scopes] - Module %1 (foo.hlt:2-11) +[debug/ast-scopes] | Bar -> declaration::ImportedModule %11 +[debug/ast-scopes] | Foo -> Module %1 +[debug/ast-scopes] | Foo1 -> declaration::Type %12 +[debug/ast-scopes] | Foo2 -> declaration::Type %13 +[debug/ast-scopes] | foo -> declaration::Function %14 +[debug/ast-scopes] - ID (foo.hlt:2) +[debug/ast-scopes] - statement::Block (foo.hlt:2-11) +[debug/ast-scopes] - declaration::ImportedModule %11 (foo.hlt:4) +[debug/ast-scopes] | Bar -> Module %2 +[debug/ast-scopes] | Bar1 -> declaration::Type %8 +[debug/ast-scopes] | Bar2 -> declaration::Type %9 +[debug/ast-scopes] | Foo -> declaration::ImportedModule %7 +[debug/ast-scopes] | bar -> declaration::Function %10 +[debug/ast-scopes] - ID (foo.hlt:4) +[debug/ast-scopes] - declaration::Type %12 (foo.hlt:6) +[debug/ast-scopes] - ID (foo.hlt:6) +[debug/ast-scopes] - type::Bool (foo.hlt:6) (non-const) (type-id: Foo::Foo1) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Type %13 (foo.hlt:7) +[debug/ast-scopes] - ID (foo.hlt:7) +[debug/ast-scopes] - type::String (bar.hlt:6) (non-const) (type-id: Foo::Foo2) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/ast-scopes] - declaration::Function %14 (foo.hlt:9) +[debug/ast-scopes] - Function > (foo.hlt:9) +[debug/ast-scopes] | bar -> declaration::Parameter %6 +[debug/ast-scopes] | foo -> declaration::Parameter %5 +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::Function (foo.hlt:9) (non-const) +[debug/ast-scopes] - type::function::Result (foo.hlt:9) +[debug/ast-scopes] - type::String (foo.hlt:9) (non-const) +[debug/ast-scopes] - declaration::Parameter %5 (foo.hlt:9) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: bool) (foo.hlt:9) (non-const) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - declaration::Parameter %6 (foo.hlt:9) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - type::ResolvedID (type: string) (foo.hlt:9) (non-const) +[debug/ast-scopes] - ID (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:9) +[debug/ast-scopes] - node::None (foo.hlt:6) +[debug/compiler] resolving IDs in module Bar +[debug/compiler] resolving IDs in module Foo +[debug/compiler] resolving operators in module Bar +[debug/compiler] resolving operators in module Foo +[debug/compiler] coercing expressions for Bar +[debug/compiler] coercing expressions for Foo +[debug/ast-final] # Foo: Final AST +[debug/ast-final] - Module %1 (foo.hlt:2-11) +[debug/ast-final] | Bar -> declaration::ImportedModule %11 +[debug/ast-final] | Foo -> Module %1 +[debug/ast-final] | Foo1 -> declaration::Type %12 +[debug/ast-final] | Foo2 -> declaration::Type %13 +[debug/ast-final] | foo -> declaration::Function %14 +[debug/ast-final] - ID (foo.hlt:2) +[debug/ast-final] - statement::Block (foo.hlt:2-11) +[debug/ast-final] - declaration::ImportedModule %11 (foo.hlt:4) +[debug/ast-final] | Bar -> Module %2 +[debug/ast-final] | Bar1 -> declaration::Type %8 +[debug/ast-final] | Bar2 -> declaration::Type %9 +[debug/ast-final] | Foo -> declaration::ImportedModule %7 +[debug/ast-final] | bar -> declaration::Function %10 +[debug/ast-final] - ID (foo.hlt:4) +[debug/ast-final] - declaration::Type %12 (foo.hlt:6) +[debug/ast-final] - ID (foo.hlt:6) +[debug/ast-final] - type::Bool (foo.hlt:6) (non-const) (type-id: Foo::Foo1) +[debug/ast-final] - node::None (foo.hlt:6) +[debug/ast-final] - declaration::Type %13 (foo.hlt:7) +[debug/ast-final] - ID (foo.hlt:7) +[debug/ast-final] - type::String (bar.hlt:6) (non-const) (type-id: Foo::Foo2) +[debug/ast-final] - node::None (foo.hlt:6) +[debug/ast-final] - declaration::Function %14 (foo.hlt:9) +[debug/ast-final] - Function > (foo.hlt:9) +[debug/ast-final] | bar -> declaration::Parameter %6 +[debug/ast-final] | foo -> declaration::Parameter %5 +[debug/ast-final] - ID (foo.hlt:9) +[debug/ast-final] - type::Function (foo.hlt:9) (non-const) +[debug/ast-final] - type::function::Result (foo.hlt:9) +[debug/ast-final] - type::String (foo.hlt:9) (non-const) +[debug/ast-final] - declaration::Parameter %5 (foo.hlt:9) +[debug/ast-final] - ID (foo.hlt:9) +[debug/ast-final] - type::ResolvedID (type: bool) (foo.hlt:9) (non-const) +[debug/ast-final] - ID (foo.hlt:9) +[debug/ast-final] - node::None (foo.hlt:9) +[debug/ast-final] - declaration::Parameter %6 (foo.hlt:9) +[debug/ast-final] - ID (foo.hlt:9) +[debug/ast-final] - type::ResolvedID (type: string) (foo.hlt:9) (non-const) +[debug/ast-final] - ID (foo.hlt:9) +[debug/ast-final] - node::None (foo.hlt:9) +[debug/ast-final] - node::None (foo.hlt:9) +[debug/ast-final] - node::None (foo.hlt:6) +[debug/compiler] validating module Bar (post-transform) +[debug/compiler] validating module Foo (post-transform) +[debug/compiler] updated cached AST for module Bar (final: yes, requires_compilation: no, dependencies: Foo) +[debug/compiler] updated cached AST for module Foo (final: yes, requires_compilation: yes, dependencies: Bar) +[debug/compiler] compiling module Foo to C++ +[debug/compiler] importing declarations from module Bar +[debug/compiler] finalizing module Foo +// Begin of Foo (from "foo.hlt") +// Compiled by HILTI version 0.3.0-branch + +#include + +#include + +namespace __hlt::Foo { + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.ast.imported-id/foo.hlt","version":1} +*/ + diff --git a/tests/Baseline/hilti.ast.types/output b/tests/Baseline/hilti.ast.types/output new file mode 100644 index 000000000..020bd7bc2 --- /dev/null +++ b/tests/Baseline/hilti.ast.types/output @@ -0,0 +1,42 @@ +[debug/compiler] parsing file "/home/robin/work/spicy/tests/.tmp/hilti.ast.types/types.hlt" +[debug/compiler] registering AST for module Foo ("/home/robin/work/spicy/tests/.tmp/hilti.ast.types/types.hlt") +[debug/compiler] processing AST, round 1 +[debug/compiler] performing missing imports for module Foo +[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: (none)) +[debug/compiler] modules: Foo +[debug/compiler] resetting nodes for module Foo +[debug/compiler] building scopes for all module modules +[debug/compiler] resolving IDs in module Foo +[debug/compiler] resolving operators in module Foo +[debug/compiler] coercing expressions for Foo +[debug/ast-final] # Foo: Final AST +[debug/ast-final] - Module %1 (types.hlt:5-10) +[debug/ast-final] | Foo -> Module %1 +[debug/ast-final] | x1 -> declaration::GlobalVariable %2 +[debug/ast-final] | x2 -> declaration::GlobalVariable %3 +[debug/ast-final] - ID (types.hlt:5) +[debug/ast-final] - statement::Block (types.hlt:5-10) +[debug/ast-final] - declaration::GlobalVariable %2 (types.hlt:5-7) +[debug/ast-final] - ID (types.hlt:7) +[debug/ast-final] - type::Bytes (types.hlt:5-7) (non-const) +[debug/ast-final] - expression::Ctor (types.hlt:7) +[debug/ast-final] - ctor::Bytes (types.hlt:7) +[debug/ast-final] - declaration::GlobalVariable %3 (types.hlt:7-8) +[debug/ast-final] - ID (types.hlt:8) +[debug/ast-final] - type::Tuple (types.hlt:8) (non-const) +[debug/ast-final] - type::UnsignedInteger (types.hlt:8) (non-const) +[debug/ast-final] - type::UnsignedInteger (types.hlt:8) (non-const) +[debug/ast-final] - expression::Ctor (types.hlt:8) +[debug/ast-final] - ctor::Tuple (types.hlt:8) +[debug/ast-final] - expression::Ctor (types.hlt:8) +[debug/ast-final] - ctor::UnsignedInteger (types.hlt:8) +[debug/ast-final] - expression::Ctor (types.hlt:8) +[debug/ast-final] - ctor::UnsignedInteger (types.hlt:8) +[debug/compiler] validating module Foo (post-transform) +[debug/compiler] updated cached AST for module Foo (final: yes, requires_compilation: yes, dependencies: (none)) +module Foo { + +global bytes x1 = b"abc"; +global tuple, uint<64>> x2 = (1, 2); + +} diff --git a/tests/Baseline/hilti.ast.validation/output b/tests/Baseline/hilti.ast.validation/output new file mode 100644 index 000000000..323e155dc --- /dev/null +++ b/tests/Baseline/hilti.ast.validation/output @@ -0,0 +1,10 @@ +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:11: integer type's width must be one of 8/16/32/64, but is 65 +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:12: integer type's width must be one of 8/16/32/64, but is 0 +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:12-14: type 'void' cannot be used for variable declaration +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:15-16: type 'void' cannot be used for variable declaration +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:19: function must return a value +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:23: cannot coerce expression '42' of type 'uint<64>' to type 'void' +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:23: void function cannot return a value +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:31: cannot coerce expression '"1" == "2"' of type 'bool' to type 'real' +[error] /Users/robin/work/spicy/tests/.tmp/hilti.ast.validation/validation.hlt:32: cannot resolve operator: == > +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.codegen.ctor/output b/tests/Baseline/hilti.codegen.ctor/output new file mode 100644 index 000000000..d2a9ac836 --- /dev/null +++ b/tests/Baseline/hilti.codegen.ctor/output @@ -0,0 +1,2 @@ +/abc/ +42 diff --git a/tests/Baseline/hilti.codegen.reserved/output b/tests/Baseline/hilti.codegen.reserved/output new file mode 100644 index 000000000..daaac9e30 --- /dev/null +++ b/tests/Baseline/hilti.codegen.reserved/output @@ -0,0 +1,2 @@ +42 +42 diff --git a/tests/Baseline/hilti.expressions.default-ctor/output b/tests/Baseline/hilti.expressions.default-ctor/output new file mode 100644 index 000000000..d25119308 --- /dev/null +++ b/tests/Baseline/hilti.expressions.default-ctor/output @@ -0,0 +1 @@ +[$a="", $b=(not set)] diff --git a/tests/Baseline/hilti.expressions.list-comprehension/output b/tests/Baseline/hilti.expressions.list-comprehension/output new file mode 100644 index 000000000..7300baa3a --- /dev/null +++ b/tests/Baseline/hilti.expressions.list-comprehension/output @@ -0,0 +1 @@ +[2, 4, 6] diff --git a/tests/Baseline/hilti.expressions.ternary/output b/tests/Baseline/hilti.expressions.ternary/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.hiltic.cc.empty/output b/tests/Baseline/hilti.hiltic.cc.empty/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.hiltic.cc.exception/output b/tests/Baseline/hilti.hiltic.cc.exception/output new file mode 100644 index 000000000..b7a954b84 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.cc.exception/output @@ -0,0 +1,2 @@ +HILTI - Begin +Caught exception: test exception (/Users/robin/work/spicy/tests/.tmp/hilti.hiltic.cc.exception/exception.hlt:20) diff --git a/tests/Baseline/hilti.hiltic.cc.hello-world/output b/tests/Baseline/hilti.hiltic.cc.hello-world/output new file mode 100644 index 000000000..af5626b4a --- /dev/null +++ b/tests/Baseline/hilti.hiltic.cc.hello-world/output @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/Baseline/hilti.hiltic.cc.import/output b/tests/Baseline/hilti.hiltic.cc.import/output new file mode 100644 index 000000000..522e7b3cf --- /dev/null +++ b/tests/Baseline/hilti.hiltic.cc.import/output @@ -0,0 +1,6 @@ +Hello, world! +Foo! +Bar! +Hello, world! +Foo! +Bar! diff --git a/tests/Baseline/hilti.hiltic.cc.separate-units/output b/tests/Baseline/hilti.hiltic.cc.separate-units/output new file mode 100644 index 000000000..9f20cb124 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.cc.separate-units/output @@ -0,0 +1,2 @@ +Hello, world 1! +Hello, world 2! diff --git a/tests/Baseline/hilti.hiltic.cc.yield/output b/tests/Baseline/hilti.hiltic.cc.yield/output new file mode 100644 index 000000000..d6278cc69 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.cc.yield/output @@ -0,0 +1,8 @@ +HILTI - 1 - argument: argument +suspended, resuming +HILTI - 2 +suspended, resuming +HILTI - 3 +suspended, resuming +HILTI - Done +Done, result: test-result diff --git a/tests/Baseline/hilti.hiltic.jit.broken-cxx/output b/tests/Baseline/hilti.hiltic.jit.broken-cxx/output new file mode 100644 index 000000000..f14c680b0 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.broken-cxx/output @@ -0,0 +1,11 @@ +/home/robin/work/spicy/tests/.tmp/hilti.hiltic.jit.broken-cxx/broken-cxx.cc:6:1: error: unknown type name 'Broken' +Broken C++. +^ +/home/robin/work/spicy/tests/.tmp/hilti.hiltic.jit.broken-cxx/broken-cxx.cc:6:9: error: expected ';' after top level declarator +Broken C++. + ^ + ; +2 errors generated. +[error] jit: failed to execute compilation action. +[error] jit: failed to compile C++ file "/home/robin/work/spicy/tests/.tmp/hilti.hiltic.jit.broken-cxx/broken-cxx.cc" to bitcode +[error] hiltic: JIT compilation failed diff --git a/tests/Baseline/hilti.hiltic.jit.broken-hilti-linking/output b/tests/Baseline/hilti.hiltic.jit.broken-hilti-linking/output new file mode 100644 index 000000000..10053d9fa --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.broken-hilti-linking/output @@ -0,0 +1,11 @@ +Test.cc:15:44: error: use of undeclared identifier 'does_not_exist'; did you mean 'hilti::rt::__does_not_exist'? +extern void __hlt::Test::__init_module() { does_not_exist(); } + ^~~~~~~~~~~~~~ + hilti::rt::__does_not_exist +/home/robin/work/spicy/hilti/include/hilti/rt/hilti.h:40:13: note: 'hilti::rt::__does_not_exist' declared here +extern void __does_not_exist(); + ^ +1 error generated. +[error] jit: failed to execute compilation action. +[error] jit: failed to compile C++ code unit Test to bitcode +[error] hiltic: JIT compilation failed diff --git a/tests/Baseline/hilti.hiltic.jit.empty/output b/tests/Baseline/hilti.hiltic.jit.empty/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.hiltic.jit.exception/output b/tests/Baseline/hilti.hiltic.jit.exception/output new file mode 100644 index 000000000..34cf83161 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.exception/output @@ -0,0 +1,2 @@ +HILTI - Begin +Caught exception: test exception (/home/robin/work/spicy/tests/.tmp/hilti.hiltic.jit.exception/exception.hlt:13) diff --git a/tests/Baseline/hilti.hiltic.jit.globals/output b/tests/Baseline/hilti.hiltic.jit.globals/output new file mode 100644 index 000000000..af5626b4a --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.globals/output @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/Baseline/hilti.hiltic.jit.hello-world/output b/tests/Baseline/hilti.hiltic.jit.hello-world/output new file mode 100644 index 000000000..af5626b4a --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.hello-world/output @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/Baseline/hilti.hiltic.jit.import/output b/tests/Baseline/hilti.hiltic.jit.import/output new file mode 100644 index 000000000..0a4a445b1 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.import/output @@ -0,0 +1,6 @@ +Bar! +Bar! +Foo! +Foo! +Hello, world from Bar! +Hello, world from Foo! diff --git a/tests/Baseline/hilti.hiltic.jit.precompiled-globals-twice/output b/tests/Baseline/hilti.hiltic.jit.precompiled-globals-twice/output new file mode 100644 index 000000000..95e75d98d --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.precompiled-globals-twice/output @@ -0,0 +1,2 @@ +Hello, world! +Hello, world! diff --git a/tests/Baseline/hilti.hiltic.jit.precompiled-globals/output b/tests/Baseline/hilti.hiltic.jit.precompiled-globals/output new file mode 100644 index 000000000..af5626b4a --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.precompiled-globals/output @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/Baseline/hilti.hiltic.jit.precompiled-hello-world/output b/tests/Baseline/hilti.hiltic.jit.precompiled-hello-world/output new file mode 100644 index 000000000..af5626b4a --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.precompiled-hello-world/output @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/Baseline/hilti.hiltic.jit.yield/output b/tests/Baseline/hilti.hiltic.jit.yield/output new file mode 100644 index 000000000..d6278cc69 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.jit.yield/output @@ -0,0 +1,8 @@ +HILTI - 1 - argument: argument +suspended, resuming +HILTI - 2 +suspended, resuming +HILTI - 3 +suspended, resuming +HILTI - Done +Done, result: test-result diff --git a/tests/Baseline/hilti.hiltic.print.empty/output b/tests/Baseline/hilti.hiltic.print.empty/output new file mode 100644 index 000000000..235c9c981 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.print.empty/output @@ -0,0 +1,30 @@ +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.empty/empty.hlt") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include + +namespace __hlt::Foo { + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.empty/empty.hlt","version":1} +*/ + +// Begin of __linker__ +// Compiled by HILTI version 0.2.0-dev + +#include + +// +// Linker code generated for modules: +// - Foo (/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.empty/empty.hlt) + +#include + diff --git a/tests/Baseline/hilti.hiltic.print.globals/output b/tests/Baseline/hilti.hiltic.print.globals/output new file mode 100644 index 000000000..3b66b09cf --- /dev/null +++ b/tests/Baseline/hilti.hiltic.print.globals/output @@ -0,0 +1,46 @@ +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.globals/globals.hlt") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include + +namespace __hlt::Foo { + using __globals_t = struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { + std::string X; + template void __visit(F _) const { _("X", X); } + }; + + inline unsigned int __globals_index; + static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } + extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_module(); + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__init_globals(hilti::rt::Context* ctx) { + hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); + __globals()->X = std::string("Hello, world!"); +} + +extern void __hlt::Foo::__init_module() { hilti::rt::print(Foo::__globals()->X, true); } + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.globals/globals.hlt","version":1} +*/ + +// Begin of __linker__ +// Compiled by HILTI version 0.2.0-dev + +#include + +// +// Linker code generated for modules: +// - Foo (/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.globals/globals.hlt) + +#include + diff --git a/tests/Baseline/hilti.hiltic.print.hello-world/output b/tests/Baseline/hilti.hiltic.print.hello-world/output new file mode 100644 index 000000000..1beb61902 --- /dev/null +++ b/tests/Baseline/hilti.hiltic.print.hello-world/output @@ -0,0 +1,33 @@ +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.hello-world/hello-world.hlt") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include + +namespace __hlt::Foo { + extern void __init_module(); + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__init_module() { hilti::rt::print(std::string("Hello, world!"), true); } + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, nullptr, nullptr}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.hello-world/hello-world.hlt","version":1} +*/ + +// Begin of __linker__ +// Compiled by HILTI version 0.2.0-dev + +#include + +// +// Linker code generated for modules: +// - Foo (/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.hello-world/hello-world.hlt) + +#include + diff --git a/tests/Baseline/hilti.hiltic.print.import/output b/tests/Baseline/hilti.hiltic.print.import/output new file mode 100644 index 000000000..d37c7255b --- /dev/null +++ b/tests/Baseline/hilti.hiltic.print.import/output @@ -0,0 +1,118 @@ +// Begin of Foo (from "foo.hlt") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include + +namespace __hlt::Bar { + using __globals_t = struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { + std::string bar; + template void __visit(F _) const { _("bar", bar); } + }; + + inline unsigned int __globals_index; + static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } + extern void __init_globals(hilti::rt::Context* ctx); +} + +namespace __hlt::Foo { + using __globals_t = struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { + std::string foo; + template void __visit(F _) const { _("foo", foo); } + }; + + inline unsigned int __globals_index; + static inline auto __globals() { + return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); + } + + extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_module(); + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__init_globals(hilti::rt::Context* ctx) { + hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); + __globals()->foo = std::string("Foo!"); +} + +extern void __hlt::Foo::__init_module() { + hilti::rt::print(std::string("Hello, world from Foo!"), true); + hilti::rt::print(Foo::__globals()->foo, true); + hilti::rt::print(Bar::__globals()->bar, true); +} + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.import/foo.hlt","version":1} +*/ + +// Begin of Bar (from "bar.hlt") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include + +namespace __hlt::Bar { + using __globals_t = struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { + std::string bar; + template void __visit(F _) const { _("bar", bar); } + }; + + inline unsigned int __globals_index; + static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } + extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_module(); + extern void __register_module(); +} + +namespace __hlt::Foo { + using __globals_t = struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { + std::string foo; + template void __visit(F _) const { _("foo", foo); } + }; + + inline unsigned int __globals_index; + static inline auto __globals() { + return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); + } + + extern void __init_globals(hilti::rt::Context* ctx); +} + +HILTI_PRE_INIT(__hlt::Bar::__register_module) + +extern void __hlt::Bar::__init_globals(hilti::rt::Context* ctx) { + hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); + __globals()->bar = std::string("Bar!"); +} + +extern void __hlt::Bar::__init_module() { + hilti::rt::print(std::string("Hello, world from Bar!"), true); + hilti::rt::print(Foo::__globals()->foo, true); + hilti::rt::print(Bar::__globals()->bar, true); +} + +extern void __hlt::Bar::__register_module() { hilti::rt::detail::registerModule({ "Bar", &__init_module, &__init_globals, &__globals_index}); } + +/* __HILTI_LINKER_V1__ +{"module":"Bar","namespace":"__hlt::Bar","path":"/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.import/bar.hlt","version":1} +*/ + +// Begin of __linker__ +// Compiled by HILTI version 0.2.0-dev + +#include + +// +// Linker code generated for modules: +// - Bar (/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.import/bar.hlt) +// - Foo (/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.import/foo.hlt) + +#include + diff --git a/tests/Baseline/hilti.hiltic.print.yield/output b/tests/Baseline/hilti.hiltic.print.yield/output new file mode 100644 index 000000000..297cfb42c --- /dev/null +++ b/tests/Baseline/hilti.hiltic.print.yield/output @@ -0,0 +1,46 @@ +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.yield/yield.hlt") +// Compiled by HILTI version 0.3.0-branch + +#include + +#include + +namespace __hlt::Foo { + extern void __register_module(); + extern auto test(const std::string& x) -> std::string; +} + +namespace hlt::Foo { + extern auto test(const std::string& x) -> hilti::rt::Resumable; +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } + +extern auto __hlt::Foo::test(const std::string& x) -> std::string { + hilti::rt::print(std::string("HILTI - 1 - argument: "), false); + hilti::rt::print(x, true); + hilti::rt::detail::yield(); + hilti::rt::print(std::string("HILTI - 2"), true); + hilti::rt::detail::yield(); + hilti::rt::print(std::string("HILTI - 3"), true); + hilti::rt::detail::yield(); + hilti::rt::print(std::string("HILTI - Done"), true); + return std::string("test-result"); +} + +extern auto hlt::Foo::test(const std::string& x) -> hilti::rt::Resumable { + auto cb = [&](hilti::rt::resumable::Handle* r) -> std::any { + return __hlt::Foo::test(x); + }; + + hilti::rt::Resumable r = {std::move(cb)}; + r.run(); + return r; +} + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.hiltic.print.yield/yield.hlt","version":1} +*/ + diff --git a/tests/Baseline/hilti.hiltic.print.yield/prototypes b/tests/Baseline/hilti.hiltic.print.yield/prototypes new file mode 100644 index 000000000..c8b28947e --- /dev/null +++ b/tests/Baseline/hilti.hiltic.print.yield/prototypes @@ -0,0 +1,16 @@ +// Prototypes for module Foo + +#ifndef HILTI_PROTOTYPES_FOO_H + +#include + +namespace __hlt::Foo { + extern void __register_module(); + extern auto test(const std::string& x) -> std::string; +} + +namespace hlt::Foo { + extern auto test(const std::string& x) -> hilti::rt::Resumable; +} + +#endif diff --git a/tests/Baseline/hilti.rt.debug/output b/tests/Baseline/hilti.rt.debug/output new file mode 100644 index 000000000..2865e4c86 --- /dev/null +++ b/tests/Baseline/hilti.rt.debug/output @@ -0,0 +1,2 @@ +[test-debug] 12 +[test-debug] 12 diff --git a/tests/Baseline/hilti.rt.exception-location/output b/tests/Baseline/hilti.rt.exception-location/output new file mode 100644 index 000000000..039e5d0e8 --- /dev/null +++ b/tests/Baseline/hilti.rt.exception-location/output @@ -0,0 +1 @@ +uncaught exception hilti::rt::InvalidIterator: bound object has expired (/Users/robin/work/spicy/tests/.tmp/hilti.rt.exception-location/exception-location.hlt:11) diff --git a/tests/Baseline/hilti.rt.print-escapes/output b/tests/Baseline/hilti.rt.print-escapes/output new file mode 100644 index 000000000..5747bb6a4 --- /dev/null +++ b/tests/Baseline/hilti.rt.print-escapes/output @@ -0,0 +1,9 @@ +Test + +Test\n +["Test\n"] +[b"Test\n"] +Test + +Test + diff --git a/tests/Baseline/hilti.rt.print/output b/tests/Baseline/hilti.rt.print/output new file mode 100644 index 000000000..1760d87c7 --- /dev/null +++ b/tests/Baseline/hilti.rt.print/output @@ -0,0 +1,3 @@ +abc +a"bc +[$s1="Foo!", $s2="a\"b\n"] diff --git a/tests/Baseline/hilti.statements.assert/output b/tests/Baseline/hilti.statements.assert/output new file mode 100644 index 000000000..eda7c00fe --- /dev/null +++ b/tests/Baseline/hilti.statements.assert/output @@ -0,0 +1,3 @@ +uncaught exception hilti::rt::AssertionFailure: failed expression 'False' (false1.hlt:1) +uncaught exception hilti::rt::AssertionFailure: Test message (false2.hlt:1) +uncaught exception hilti::rt::AssertionFailure: Test message for missing exception (false3.hlt:1) diff --git a/tests/Baseline/hilti.statements.break/output b/tests/Baseline/hilti.statements.break/output new file mode 100644 index 000000000..6ed281c75 --- /dev/null +++ b/tests/Baseline/hilti.statements.break/output @@ -0,0 +1,2 @@ +1 +1 diff --git a/tests/Baseline/hilti.statements.continue/output b/tests/Baseline/hilti.statements.continue/output new file mode 100644 index 000000000..2c9d316b3 --- /dev/null +++ b/tests/Baseline/hilti.statements.continue/output @@ -0,0 +1,6 @@ +1 +2 +3 +1 +2 +3 diff --git a/tests/Baseline/hilti.statements.for/output b/tests/Baseline/hilti.statements.for/output new file mode 100644 index 000000000..2c9d316b3 --- /dev/null +++ b/tests/Baseline/hilti.statements.for/output @@ -0,0 +1,6 @@ +1 +2 +3 +1 +2 +3 diff --git a/tests/Baseline/hilti.statements.if/output b/tests/Baseline/hilti.statements.if/output new file mode 100644 index 000000000..cab5d0128 --- /dev/null +++ b/tests/Baseline/hilti.statements.if/output @@ -0,0 +1,12 @@ +1 - correct +2a - correct +2b - correct +3 - correct +2a - correct +2b - correct +3 - correct +(4 should be missing) +5 - correct - 42 +6 - correct - 0 +7 - correct - foo +8 - correct - foo diff --git a/tests/Baseline/hilti.statements.switch/output b/tests/Baseline/hilti.statements.switch/output new file mode 100644 index 000000000..a292926e8 --- /dev/null +++ b/tests/Baseline/hilti.statements.switch/output @@ -0,0 +1,11 @@ +1A +1B +1B +1C +1* +2B +2B +2B +2* +2* +1T diff --git a/tests/Baseline/hilti.statements.throw/output b/tests/Baseline/hilti.statements.throw/output new file mode 100644 index 000000000..6affb9b07 --- /dev/null +++ b/tests/Baseline/hilti.statements.throw/output @@ -0,0 +1 @@ +uncaught exception hilti::rt::IndexError: my message (/home/robin/work/spicy/tests/.tmp/hilti.statements.throw/throw.hlt:8) diff --git a/tests/Baseline/hilti.statements.try-across-hooks/output b/tests/Baseline/hilti.statements.try-across-hooks/output new file mode 100644 index 000000000..0a0d5e610 --- /dev/null +++ b/tests/Baseline/hilti.statements.try-across-hooks/output @@ -0,0 +1,3 @@ +Foo: f1 +Bar: f1 +("Caught: ", "Bar::f1") diff --git a/tests/Baseline/hilti.statements.try/output b/tests/Baseline/hilti.statements.try/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.statements.while/output b/tests/Baseline/hilti.statements.while/output new file mode 100644 index 000000000..2893490c7 --- /dev/null +++ b/tests/Baseline/hilti.statements.while/output @@ -0,0 +1,30 @@ +1 - 0 +1 - 1 +1 - 2 +1 - 3 +1 - 4 +2 - 0 +2 - 1 +2 - 2 +2 - 3 +2 - 4 +3 - -4 +3 - -3 +3 - -2 +3 - -1 +4 - 0 +4 - 1 +4 - 2 +4 - 3 +4 - 4 +4 - done - 5 +5 - 1 +5 - 2 +5 - 3 +5 - 4 +5 - done - 5 +6 - -4 +6 - -3 +6 - -2 +6 - -1 +6 - done - 0 diff --git a/tests/Baseline/hilti.tools.hilti-driver-hilti-path/output b/tests/Baseline/hilti.tools.hilti-driver-hilti-path/output new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/tests/Baseline/hilti.tools.hilti-driver-hilti-path/output @@ -0,0 +1 @@ +foo diff --git a/tests/Baseline/hilti.types.address.ops/output b/tests/Baseline/hilti.types.address.ops/output new file mode 100644 index 000000000..4a1d7cf17 --- /dev/null +++ b/tests/Baseline/hilti.types.address.ops/output @@ -0,0 +1,5 @@ +1.2.3.4 +2001:db8:85a3:8d3:1319:8a2e:370:7348 +1.2.3.4 +AddressFamily::IPv4 +AddressFamily::IPv6 diff --git a/tests/Baseline/hilti.types.address.unpack/output b/tests/Baseline/hilti.types.address.unpack/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.bytes.decode/output b/tests/Baseline/hilti.types.bytes.decode/output new file mode 100644 index 000000000..f1231cf8a --- /dev/null +++ b/tests/Baseline/hilti.types.bytes.decode/output @@ -0,0 +1,3 @@ +testing +test��ng� +testüng diff --git a/tests/Baseline/hilti.types.bytes.iterator/output b/tests/Baseline/hilti.types.bytes.iterator/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.bytes.join/output b/tests/Baseline/hilti.types.bytes.join/output new file mode 100644 index 000000000..253baa59d --- /dev/null +++ b/tests/Baseline/hilti.types.bytes.join/output @@ -0,0 +1,7 @@ +foobarbaz +foo.bar.baz +fooXXXbarXXXbaz + +one.two.three +1.2.3 +1 diff --git a/tests/Baseline/hilti.types.bytes.match/output b/tests/Baseline/hilti.types.bytes.match/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.bytes.ops/output b/tests/Baseline/hilti.types.bytes.ops/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.bytes.to-int/output b/tests/Baseline/hilti.types.bytes.to-int/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.bytes.to-time/output b/tests/Baseline/hilti.types.bytes.to-time/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.bytes.view/output b/tests/Baseline/hilti.types.bytes.view/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.enum.basic/output b/tests/Baseline/hilti.types.enum.basic/output new file mode 100644 index 000000000..675774360 --- /dev/null +++ b/tests/Baseline/hilti.types.enum.basic/output @@ -0,0 +1,5 @@ +X::A1 +Y::Undef +X::A1 +Y::B2 +[$x1=X::Undef, $x2=(not set), $y1=(not set)] diff --git a/tests/Baseline/hilti.types.enum.cast/output b/tests/Baseline/hilti.types.enum.cast/output new file mode 100644 index 000000000..f9898177a --- /dev/null +++ b/tests/Baseline/hilti.types.enum.cast/output @@ -0,0 +1,5 @@ +X::Undef +X::A1 +X::A2 +X:: +X:: diff --git a/tests/Baseline/hilti.types.enum.equal-ops-fail/output b/tests/Baseline/hilti.types.enum.equal-ops-fail/output new file mode 100644 index 000000000..3213e60e3 --- /dev/null +++ b/tests/Baseline/hilti.types.enum.equal-ops-fail/output @@ -0,0 +1,3 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.enum.equal-ops-fail/equal-ops-fail.hlt:11: cannot resolve operator: != +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.enum.equal-ops-fail/equal-ops-fail.hlt:15: cannot resolve operator: != +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.enum.equal-ops/output b/tests/Baseline/hilti.types.enum.equal-ops/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.enum.import/output b/tests/Baseline/hilti.types.enum.import/output new file mode 100644 index 000000000..2e3def6ea --- /dev/null +++ b/tests/Baseline/hilti.types.enum.import/output @@ -0,0 +1,2 @@ +X::A1 +X::A2 diff --git a/tests/Baseline/hilti.types.function.hook-functions-across-units/output b/tests/Baseline/hilti.types.function.hook-functions-across-units/output new file mode 100644 index 000000000..af65f6a45 --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook-functions-across-units/output @@ -0,0 +1,3 @@ +Foo: f1 +Bar: f1 +Bar: f2 diff --git a/tests/Baseline/hilti.types.function.hook-functions-empty/output b/tests/Baseline/hilti.types.function.hook-functions-empty/output new file mode 100644 index 000000000..d7639e053 --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook-functions-empty/output @@ -0,0 +1 @@ +(not set) diff --git a/tests/Baseline/hilti.types.function.hook-functions/output b/tests/Baseline/hilti.types.function.hook-functions/output new file mode 100644 index 000000000..cbf78494a --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook-functions/output @@ -0,0 +1,4 @@ +42 +f1.a X +f1.b X +f1.c X diff --git a/tests/Baseline/hilti.types.function.hook-methods-across-units/output b/tests/Baseline/hilti.types.function.hook-methods-across-units/output new file mode 100644 index 000000000..af65f6a45 --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook-methods-across-units/output @@ -0,0 +1,3 @@ +Foo: f1 +Bar: f1 +Bar: f2 diff --git a/tests/Baseline/hilti.types.function.hook-methods-empty/output b/tests/Baseline/hilti.types.function.hook-methods-empty/output new file mode 100644 index 000000000..d7639e053 --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook-methods-empty/output @@ -0,0 +1 @@ +(not set) diff --git a/tests/Baseline/hilti.types.function.hook-methods/output b/tests/Baseline/hilti.types.function.hook-methods/output new file mode 100644 index 000000000..bb5dea31c --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook-methods/output @@ -0,0 +1,5 @@ +("in f2:", "sss") +42 +f1 sss Hurz +f2 sss Hurz +f3 sss Hurz diff --git a/tests/Baseline/hilti.types.function.hook/output b/tests/Baseline/hilti.types.function.hook/output new file mode 100644 index 000000000..dbc452c20 --- /dev/null +++ b/tests/Baseline/hilti.types.function.hook/output @@ -0,0 +1,4 @@ +f1.a X +f1.b X +f1.c X +42 diff --git a/tests/Baseline/hilti.types.id.validation/output b/tests/Baseline/hilti.types.id.validation/output new file mode 100644 index 000000000..6351718a3 --- /dev/null +++ b/tests/Baseline/hilti.types.id.validation/output @@ -0,0 +1,6 @@ +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.types.id.validation/validation.hlt:6: Invalid ID 'a-b': cannot contain '-' +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.types.id.validation/validation.hlt:7: Invalid ID '__private_name': cannot start with '__' +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.types.id.validation/validation.hlt:9: Invalid ID 'Bar-Baz': cannot contain '-' +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.types.id.validation/validation.hlt:14: Invalid ID 'a-b': cannot contain '-' +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.types.id.validation/validation.hlt:17: Invalid ID 'f-1': cannot contain '-' +[error] hiltic: parse error diff --git a/tests/Baseline/hilti.types.integer.cast/output b/tests/Baseline/hilti.types.integer.cast/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.integer.coercion-fail/output b/tests/Baseline/hilti.types.integer.coercion-fail/output new file mode 100644 index 000000000..5b9e9ca4a --- /dev/null +++ b/tests/Baseline/hilti.types.integer.coercion-fail/output @@ -0,0 +1,18 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:8-12: cannot coerce expression '256' of type 'uint<64>' to type 'uint<8>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:12-13: cannot coerce expression '65536' of type 'uint<64>' to type 'uint<16>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:13-14: cannot coerce expression '4294967296' of type 'uint<64>' to type 'uint<32>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:14-18: cannot coerce expression '-1' of type 'int<64>' to type 'uint<8>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:18-19: cannot coerce expression '-1' of type 'int<64>' to type 'uint<16>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:19-20: cannot coerce expression '-1' of type 'int<64>' to type 'uint<32>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:20-21: cannot coerce expression '-1' of type 'int<64>' to type 'uint<64>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:21-23: cannot coerce expression '128' of type 'uint<64>' to type 'int<8>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:23-24: cannot coerce expression '-129' of type 'int<64>' to type 'int<8>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:24-26: cannot coerce expression '32768' of type 'uint<64>' to type 'int<16>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:26-27: cannot coerce expression '-32769' of type 'int<64>' to type 'int<16>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:27-29: cannot coerce expression '2147483648' of type 'uint<64>' to type 'int<32>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:29-30: cannot coerce expression '-2147483649' of type 'int<64>' to type 'int<32>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:30-32: cannot coerce expression '9223372036854775808' of type 'uint<64>' to type 'int<64>' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:32-38: cannot coerce expression '9007199254740993' of type 'uint<64>' to type 'real' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:38-39: cannot coerce expression '-9007199254740993' of type 'int<64>' to type 'real' +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.coercion-fail/coercion-fail.hlt:41-43: cannot coerce expression 'Foo::i' of type 'int<32>' to type 'bool' +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.integer.coercion/output b/tests/Baseline/hilti.types.integer.coercion/output new file mode 100644 index 000000000..7928ab8b5 --- /dev/null +++ b/tests/Baseline/hilti.types.integer.coercion/output @@ -0,0 +1 @@ +-9223372036854775808 diff --git a/tests/Baseline/hilti.types.integer.exceptions/output b/tests/Baseline/hilti.types.integer.exceptions/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log b/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log new file mode 100644 index 000000000..f7252e4bd --- /dev/null +++ b/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.integer.mixed-operands-overflow/mixed-operands-overflow.hlt:7: cannot resolve operator: > + > +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.integer.on-heap/output b/tests/Baseline/hilti.types.integer.on-heap/output new file mode 100644 index 000000000..5ad75e38a --- /dev/null +++ b/tests/Baseline/hilti.types.integer.on-heap/output @@ -0,0 +1,2 @@ +256 +128 diff --git a/tests/Baseline/hilti.types.integer.operators-signed/output b/tests/Baseline/hilti.types.integer.operators-signed/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.integer.operators-unsigned/output b/tests/Baseline/hilti.types.integer.operators-unsigned/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.integer.operators/output b/tests/Baseline/hilti.types.integer.operators/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.integer.unpack/output b/tests/Baseline/hilti.types.integer.unpack/output new file mode 100644 index 000000000..6543648b5 --- /dev/null +++ b/tests/Baseline/hilti.types.integer.unpack/output @@ -0,0 +1,4 @@ +L 0x0100 +B 0x0001 +L 0x0100 +B 0x0001 diff --git a/tests/Baseline/hilti.types.interval.ops/output b/tests/Baseline/hilti.types.interval.ops/output new file mode 100644 index 000000000..d119c4f34 --- /dev/null +++ b/tests/Baseline/hilti.types.interval.ops/output @@ -0,0 +1,2 @@ +2.500000s +1.500000s diff --git a/tests/Baseline/hilti.types.list.ctor/output b/tests/Baseline/hilti.types.list.ctor/output new file mode 100644 index 000000000..58e8afd99 --- /dev/null +++ b/tests/Baseline/hilti.types.list.ctor/output @@ -0,0 +1,11 @@ +[1, 2, 3] +[] +[1] +[1, 2, 3] +["s1", "s2"] +[[1, 2], [3, 4], [4, 5]] +[1] +[] +[1] +["s1", "s2"] +[[1, 2], [3, 4], [4, 5]] diff --git a/tests/Baseline/hilti.types.map.ops/output b/tests/Baseline/hilti.types.map.ops/output new file mode 100644 index 000000000..a926b914a --- /dev/null +++ b/tests/Baseline/hilti.types.map.ops/output @@ -0,0 +1,3 @@ +{1: AAA, 2: BBB, 3: CCC} +{} +b"BBB" diff --git a/tests/Baseline/hilti.types.network.ops/output b/tests/Baseline/hilti.types.network.ops/output new file mode 100644 index 000000000..2396c447d --- /dev/null +++ b/tests/Baseline/hilti.types.network.ops/output @@ -0,0 +1,13 @@ +2001:db8::/48 +192.168.1.0/24 +192.168.1.0/24 +AddressFamily::IPv6 +255.255.255.0 +255.255.255.255 +0.0.0.0 +ff00:: +ffff:ffff:ffff:ff00:: +ffff:ffff:ffff:ffff:: +ffff:ffff:ffff:ffff:ff00:: +ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00 +ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff diff --git a/tests/Baseline/hilti.types.optional.consts/output b/tests/Baseline/hilti.types.optional.consts/output new file mode 100644 index 000000000..3cceaba31 --- /dev/null +++ b/tests/Baseline/hilti.types.optional.consts/output @@ -0,0 +1,5 @@ +1 +b"ABC" +b"ABC" +b"" +(not set) diff --git a/tests/Baseline/hilti.types.optional.ops/output b/tests/Baseline/hilti.types.optional.ops/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.optional.unset-assign/output b/tests/Baseline/hilti.types.optional.unset-assign/output new file mode 100644 index 000000000..fd0443bfe --- /dev/null +++ b/tests/Baseline/hilti.types.optional.unset-assign/output @@ -0,0 +1,5 @@ +WARNING: Linking two modules of different data layouts: '/Users/robin/work/hilti/build/libspicy/libspicy-rt-dbg.bc' is 'e-m:o-i64:64-f80:128-n8:16:32:64-S128' whereas '' is 'e-m:e-i64:64-f80:128-n8:16:32:64-S128' +WARNING: Linking two modules of different target triples: /Users/robin/work/hilti/build/libspicy/libspicy-rt-dbg.bc' is 'x86_64-apple-darwin14.0.0' whereas '' is 'x86_64-apple-darwin14.1.0' +WARNING: Linking two modules of different data layouts: '/Users/robin/work/hilti/build/libhilti/libhilti-rt-dbg.bc' is 'e-m:o-i64:64-f80:128-n8:16:32:64-S128' whereas '' is 'e-m:e-i64:64-f80:128-n8:16:32:64-S128' +WARNING: Linking two modules of different target triples: /Users/robin/work/hilti/build/libhilti/libhilti-rt-dbg.bc' is 'x86_64-apple-darwin14.0.0' whereas '' is 'x86_64-apple-darwin14.1.0' +hilti: uncaught exception, UndefinedValue (from :) diff --git a/tests/Baseline/hilti.types.port.ops/output b/tests/Baseline/hilti.types.port.ops/output new file mode 100644 index 000000000..422fb97d7 --- /dev/null +++ b/tests/Baseline/hilti.types.port.ops/output @@ -0,0 +1,4 @@ +80/tcp +123/udp +Protocol::TCP +Protocol::UDP diff --git a/tests/Baseline/hilti.types.real.add-2/output b/tests/Baseline/hilti.types.real.add-2/output new file mode 100644 index 000000000..1fda809d4 --- /dev/null +++ b/tests/Baseline/hilti.types.real.add-2/output @@ -0,0 +1 @@ +4.21 diff --git a/tests/Baseline/hilti.types.real.add/output b/tests/Baseline/hilti.types.real.add/output new file mode 100644 index 000000000..86d26d64c --- /dev/null +++ b/tests/Baseline/hilti.types.real.add/output @@ -0,0 +1,2 @@ +12.63 +4.21 diff --git a/tests/Baseline/hilti.types.real.coercion-fail/output b/tests/Baseline/hilti.types.real.coercion-fail/output new file mode 100644 index 000000000..374fd84c0 --- /dev/null +++ b/tests/Baseline/hilti.types.real.coercion-fail/output @@ -0,0 +1,5 @@ +[error] /home/will/repos/spicy/tests/.tmp/hilti.types.real.coercion-fail/coercion-fail.hlt:6-8: cannot coerce expression '0x1.028f5c28f5c29p+0' of type 'real' to type 'bool' +[error] /home/will/repos/spicy/tests/.tmp/hilti.types.real.coercion-fail/coercion-fail.hlt:8-9: cannot coerce expression '0x0p+0' of type 'real' to type 'bool' +[error] /home/will/repos/spicy/tests/.tmp/hilti.types.real.coercion-fail/coercion-fail.hlt:9-11: cannot coerce expression '18446744073709551615' of type 'uint<64>' to type 'real' +[error] /home/will/repos/spicy/tests/.tmp/hilti.types.real.coercion-fail/coercion-fail.hlt:11-13: cannot coerce expression '9007199254740993' of type 'uint<64>' to type 'real' +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.real.coercion/output b/tests/Baseline/hilti.types.real.coercion/output new file mode 100644 index 000000000..41bec3939 --- /dev/null +++ b/tests/Baseline/hilti.types.real.coercion/output @@ -0,0 +1 @@ +3.14159 diff --git a/tests/Baseline/hilti.types.real.inf_NaN/output b/tests/Baseline/hilti.types.real.inf_NaN/output new file mode 100644 index 000000000..3303f322c --- /dev/null +++ b/tests/Baseline/hilti.types.real.inf_NaN/output @@ -0,0 +1,5 @@ +0 +-0 +inf +-inf +nan diff --git a/tests/Baseline/hilti.types.real.limits/output b/tests/Baseline/hilti.types.real.limits/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.real.mul/output b/tests/Baseline/hilti.types.real.mul/output new file mode 100644 index 000000000..e83257d08 --- /dev/null +++ b/tests/Baseline/hilti.types.real.mul/output @@ -0,0 +1,2 @@ +4284 +100 diff --git a/tests/Baseline/hilti.types.real.nops/output b/tests/Baseline/hilti.types.real.nops/output new file mode 100644 index 000000000..4dde7d65f --- /dev/null +++ b/tests/Baseline/hilti.types.real.nops/output @@ -0,0 +1,48 @@ +module Foo { + +global real d; +global real r = 0x1.999999999999ap-4; + +assert 0x1.999999999999ap-4 == 0x1.999999999999ap-4; + +} +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/hilti.types.real.nops/nops.hlt") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include + +namespace __hlt::Foo { + using __globals_t = struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { + double d; + double r; + template void __visit(F _) const { _("d", d); _("r", r); } + }; + + inline unsigned int __globals_index; + static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } + extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_module(); + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__init_globals(hilti::rt::Context* ctx) { + hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); + __globals()->r = 0x1.999999999999ap-4; +} + +extern void __hlt::Foo::__init_module() { + if ( ! (0x1.999999999999ap-4 == 0x1.999999999999ap-4) ) { + throw hilti::rt::AssertionFailure("failed expression '0x1.999999999999ap-4 == 0x1.999999999999ap-4'", "/home/robin/work/spicy/tests/.tmp/hilti.types.real.nops/nops.hlt:11"); + } +} + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/hilti.types.real.nops/nops.hlt","version":1} +*/ + diff --git a/tests/Baseline/hilti.types.real.series/output b/tests/Baseline/hilti.types.real.series/output new file mode 100644 index 000000000..9c190095e --- /dev/null +++ b/tests/Baseline/hilti.types.real.series/output @@ -0,0 +1,11 @@ +126.1 +100 +12610 +300.238 +True +False +True +False +False +126100 +127.1 diff --git a/tests/Baseline/hilti.types.real.sub/output b/tests/Baseline/hilti.types.real.sub/output new file mode 100644 index 000000000..40fda5d2f --- /dev/null +++ b/tests/Baseline/hilti.types.real.sub/output @@ -0,0 +1,2 @@ +10 +7.5 diff --git a/tests/Baseline/hilti.types.real.unpack/output b/tests/Baseline/hilti.types.real.unpack/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.reference.strong-ref/output b/tests/Baseline/hilti.types.reference.strong-ref/output new file mode 100644 index 000000000..fed36fd30 --- /dev/null +++ b/tests/Baseline/hilti.types.reference.strong-ref/output @@ -0,0 +1,5 @@ +xyz123 +xyz123 +xyz123 +xyz123 +Null diff --git a/tests/Baseline/hilti.types.reference.weak-from-strong-ref/output b/tests/Baseline/hilti.types.reference.weak-from-strong-ref/output new file mode 100644 index 000000000..f10d5abb0 --- /dev/null +++ b/tests/Baseline/hilti.types.reference.weak-from-strong-ref/output @@ -0,0 +1,10 @@ +xyz +xyz +xyz +xyz +Null +abc + +Null +Null + diff --git a/tests/Baseline/hilti.types.reference.weak-from-value-ref-int-on-heap/output b/tests/Baseline/hilti.types.reference.weak-from-value-ref-int-on-heap/output new file mode 100644 index 000000000..ff27fccd5 --- /dev/null +++ b/tests/Baseline/hilti.types.reference.weak-from-value-ref-int-on-heap/output @@ -0,0 +1,2 @@ +("init", 32, 32) +("main", ) diff --git a/tests/Baseline/hilti.types.reference.weak-from-value-ref-int/output b/tests/Baseline/hilti.types.reference.weak-from-value-ref-int/output new file mode 100644 index 000000000..ff27fccd5 --- /dev/null +++ b/tests/Baseline/hilti.types.reference.weak-from-value-ref-int/output @@ -0,0 +1,2 @@ +("init", 32, 32) +("main", ) diff --git a/tests/Baseline/hilti.types.reference.weak-from-value-ref-struct/output b/tests/Baseline/hilti.types.reference.weak-from-value-ref-struct/output new file mode 100644 index 000000000..066e610cb --- /dev/null +++ b/tests/Baseline/hilti.types.reference.weak-from-value-ref-struct/output @@ -0,0 +1,2 @@ +("init", [$s="x", $i=21], [$s="x", $i=21]) +("main", ) diff --git a/tests/Baseline/hilti.types.regexp.big-regexp/output b/tests/Baseline/hilti.types.regexp.big-regexp/output new file mode 100644 index 000000000..5d9858aa7 --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.big-regexp/output @@ -0,0 +1,3 @@ +1 +(1, b"/etc/passwd") +[b"/etc/passwd", b"passwd"] diff --git a/tests/Baseline/hilti.types.regexp.ctor/output b/tests/Baseline/hilti.types.regexp.ctor/output new file mode 100644 index 000000000..4a2514783 --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.ctor/output @@ -0,0 +1,6 @@ +/ghi/ +/ghi/ +/abc/ | /ghi/ &nosub +/ghi/ &nosub +/ghi/ &nosub + diff --git a/tests/Baseline/hilti.types.regexp.find-nosub/output b/tests/Baseline/hilti.types.regexp.find-nosub/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.regexp.find/output b/tests/Baseline/hilti.types.regexp.find/output new file mode 100644 index 000000000..576f354d2 --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.find/output @@ -0,0 +1,10 @@ +(1, True, 1) +(2, True, 1) +(3, False, -1) +(4, True, 1) +(5, False, -1) +(6, True, 1) +(7, False, -1) +(8, False, -1) +(9, False, -1) +(10, False, 1) diff --git a/tests/Baseline/hilti.types.regexp.groups/output b/tests/Baseline/hilti.types.regexp.groups/output new file mode 100644 index 000000000..370276c8e --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.groups/output @@ -0,0 +1,2 @@ +[b"A1234X5678Y9012B", b"1234", b"5678", b"9012"] +[] diff --git a/tests/Baseline/hilti.types.regexp.incremental-spicy-style/output b/tests/Baseline/hilti.types.regexp.incremental-spicy-style/output new file mode 100644 index 000000000..f2864ee1f --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.incremental-spicy-style/output @@ -0,0 +1,6 @@ +(b"12", "=>", -1, b"") +(b"34AB", "=>", -1, b"") +(b"", "=>", -1, b"") +(b"C", "=>", -1, b"") +(b"", "=>", -1, b"") +(b"DEFGHHH%%%", "=>", 42, b"%%%") diff --git a/tests/Baseline/hilti.types.regexp.incremental/output b/tests/Baseline/hilti.types.regexp.incremental/output new file mode 100644 index 000000000..ff330070a --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.incremental/output @@ -0,0 +1,3 @@ +(-1, 4) +(-1, 2) +(1, 6) diff --git a/tests/Baseline/hilti.types.regexp.match-state/output b/tests/Baseline/hilti.types.regexp.match-state/output new file mode 100644 index 000000000..04b1471ea --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.match-state/output @@ -0,0 +1,5 @@ +(-1, 4) +(-1, 2) +(1, 6) +(-1, 6) +(1, 0) diff --git a/tests/Baseline/hilti.types.regexp.set-matching/output b/tests/Baseline/hilti.types.regexp.set-matching/output new file mode 100644 index 000000000..dfb1e0bbc --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.set-matching/output @@ -0,0 +1,16 @@ +1 +2 +3 +-1 +(1, b"Foooo") +(2, b"Baaar") +(3, b"Huuurz") +(-1, b"") +41 +42 +43 +-1 +(41, b"Foooo") +(42, b"Baaar") +(43, b"Huuurz") +(-1, b"") diff --git a/tests/Baseline/hilti.types.regexp.span/output b/tests/Baseline/hilti.types.regexp.span/output new file mode 100644 index 000000000..39cbe5314 --- /dev/null +++ b/tests/Baseline/hilti.types.regexp.span/output @@ -0,0 +1,2 @@ +(1, b"234X5678Y09") +(-1, b"") diff --git a/tests/Baseline/hilti.types.result.conversion/output b/tests/Baseline/hilti.types.result.conversion/output new file mode 100644 index 000000000..661776c03 --- /dev/null +++ b/tests/Baseline/hilti.types.result.conversion/output @@ -0,0 +1,9 @@ +yeah! + + +yeah! +trouble... +yeah! +(not set) +yes +no diff --git a/tests/Baseline/hilti.types.result.error/output b/tests/Baseline/hilti.types.result.error/output new file mode 100644 index 000000000..958f5858a --- /dev/null +++ b/tests/Baseline/hilti.types.result.error/output @@ -0,0 +1,2 @@ + +test diff --git a/tests/Baseline/hilti.types.result.exceptions-2/output b/tests/Baseline/hilti.types.result.exceptions-2/output new file mode 100644 index 000000000..be2ae6bdd --- /dev/null +++ b/tests/Baseline/hilti.types.result.exceptions-2/output @@ -0,0 +1 @@ +uncaught exception hilti::rt::result::NoError: diff --git a/tests/Baseline/hilti.types.result.exceptions/output b/tests/Baseline/hilti.types.result.exceptions/output new file mode 100644 index 000000000..458eca7ae --- /dev/null +++ b/tests/Baseline/hilti.types.result.exceptions/output @@ -0,0 +1 @@ +uncaught exception hilti::rt::result::NoResult: trouble... diff --git a/tests/Baseline/hilti.types.set.ops/output b/tests/Baseline/hilti.types.set.ops/output new file mode 100644 index 000000000..341c8ed99 --- /dev/null +++ b/tests/Baseline/hilti.types.set.ops/output @@ -0,0 +1,4 @@ +{"A", "B"} +{1, 2, 3} +{} +{} diff --git a/tests/Baseline/hilti.types.stream.iterator/output b/tests/Baseline/hilti.types.stream.iterator/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.stream.ops/output b/tests/Baseline/hilti.types.stream.ops/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.stream.view/output b/tests/Baseline/hilti.types.stream.view/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.string.fmt/output b/tests/Baseline/hilti.types.string.fmt/output new file mode 100644 index 000000000..a3bc1b883 --- /dev/null +++ b/tests/Baseline/hilti.types.string.fmt/output @@ -0,0 +1,3 @@ +foo +42 +foo 32 ff diff --git a/tests/Baseline/hilti.types.struct.attributes/output b/tests/Baseline/hilti.types.struct.attributes/output new file mode 100644 index 000000000..d11b5f38a --- /dev/null +++ b/tests/Baseline/hilti.types.struct.attributes/output @@ -0,0 +1,26 @@ +[$b=True, $s1="Foo!", $s2="@", $i=21] +[$b=True, $s1=(not set), $s2=(not set), $i=42] +[$b=False, $s1=(not set), $s2=(not set), $i=42] +-- +True +Foo! +@ +42 +. +False +-- +False +-- +True +False +False +True +-- +True +True +True +True +-- +[$b=False, $s1="Foo!", $s2="@", $i=21] +[$b=True, $s1=(not set), $s2=(not set), $i=42] +[$b=False, $s1="x", $s2="y", $i=12] diff --git a/tests/Baseline/hilti.types.struct.ctor/output b/tests/Baseline/hilti.types.struct.ctor/output new file mode 100644 index 000000000..fdfd2c225 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.ctor/output @@ -0,0 +1,2 @@ +[$b=True, $s="Foo!", $i=21] +[$b=True, $s=(not set), $i=(not set)] diff --git a/tests/Baseline/hilti.types.struct.errors-method/output b/tests/Baseline/hilti.types.struct.errors-method/output new file mode 100644 index 000000000..4c08a194f --- /dev/null +++ b/tests/Baseline/hilti.types.struct.errors-method/output @@ -0,0 +1,6 @@ +[error] /Users/robin/work/spicy/tests/.tmp/hilti.types.struct.errors-method/errors-method.hlt:15: type X does not have a method 'x' matching the signature +[error] /Users/robin/work/spicy/tests/.tmp/hilti.types.struct.errors-method/errors-method.hlt:16: type X does not have a method 'y' +[error] /Users/robin/work/spicy/tests/.tmp/hilti.types.struct.errors-method/errors-method.hlt:17: X::s is not a method +[error] /Users/robin/work/spicy/tests/.tmp/hilti.types.struct.errors-method/errors-method.hlt:18: method lacks a type namespace +[error] /Users/robin/work/spicy/tests/.tmp/hilti.types.struct.errors-method/errors-method.hlt:20: unknown ID 'self' +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.errors-no-self/output b/tests/Baseline/hilti.types.struct.errors-no-self/output new file mode 100644 index 000000000..1867b93b1 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.errors-no-self/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.struct.errors-no-self/errors-no-self.hlt:13: unknown ID 'self' +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.errors/output b/tests/Baseline/hilti.types.struct.errors/output new file mode 100644 index 000000000..c1ff67e1a --- /dev/null +++ b/tests/Baseline/hilti.types.struct.errors/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.struct.errors/errors.hlt:14: type does not have field 'DoesNotExist' +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.method/output b/tests/Baseline/hilti.types.struct.method/output new file mode 100644 index 000000000..d81cc0710 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.method/output @@ -0,0 +1 @@ +42 diff --git a/tests/Baseline/hilti.types.struct.on-heap-cyclic/output b/tests/Baseline/hilti.types.struct.on-heap-cyclic/output new file mode 100644 index 000000000..bee550c0c --- /dev/null +++ b/tests/Baseline/hilti.types.struct.on-heap-cyclic/output @@ -0,0 +1,2 @@ +[$s="X2", $y=(not set)] +[$s="Y", $x=[$s="X", $y=(not set)]] diff --git a/tests/Baseline/hilti.types.struct.on-heap/output b/tests/Baseline/hilti.types.struct.on-heap/output new file mode 100644 index 000000000..a3de47a50 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.on-heap/output @@ -0,0 +1,4 @@ +x=[$b=False, $s="Foo!", $i=21] +y=[$b=True, $s="NEW", $i=21] +s1=[$b=False, $s="Foo!", $i=21] +s2=[$b=True, $s="NEW", $i=21] diff --git a/tests/Baseline/hilti.types.struct.overloading/output b/tests/Baseline/hilti.types.struct.overloading/output new file mode 100644 index 000000000..3d3dd1c48 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.overloading/output @@ -0,0 +1,3 @@ +void +bool +string diff --git a/tests/Baseline/hilti.types.struct.params-write-fail/output b/tests/Baseline/hilti.types.struct.params-write-fail/output new file mode 100644 index 000000000..2ef217fd4 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.params-write-fail/output @@ -0,0 +1,4 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.struct.params-write-fail/params-write-fail.hlt:17: cannot assign to expression: X = True +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.struct.params-write-fail/params-write-fail.hlt:18: cannot resolve operator: add >[] +[error] /home/robin/work/spicy/tests/.tmp/hilti.types.struct.params-write-fail/params-write-fail.hlt:19: cannot assign to expression: (*Z).a = 42 +[error] spicyc: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.params/output b/tests/Baseline/hilti.types.struct.params/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.struct.static-field/output b/tests/Baseline/hilti.types.struct.static-field/output new file mode 100644 index 000000000..de2f27de7 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.static-field/output @@ -0,0 +1,4 @@ +[$a=""] + +[$a="not static"] +static diff --git a/tests/Baseline/hilti.types.struct.static-method-extern/output b/tests/Baseline/hilti.types.struct.static-method-extern/output new file mode 100644 index 000000000..12449848d --- /dev/null +++ b/tests/Baseline/hilti.types.struct.static-method-extern/output @@ -0,0 +1 @@ +Done, result: 22 diff --git a/tests/Baseline/hilti.types.struct.static-method/output b/tests/Baseline/hilti.types.struct.static-method/output new file mode 100644 index 000000000..48082f72f --- /dev/null +++ b/tests/Baseline/hilti.types.struct.static-method/output @@ -0,0 +1 @@ +12 diff --git a/tests/Baseline/hilti.types.time.functions/output b/tests/Baseline/hilti.types.time.functions/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/hilti.types.time.ops/output b/tests/Baseline/hilti.types.time.ops/output new file mode 100644 index 000000000..7f0fb9bb4 --- /dev/null +++ b/tests/Baseline/hilti.types.time.ops/output @@ -0,0 +1,3 @@ +2011-01-19T05:31:50.500000000Z +2011-01-19T06:31:50.000000000Z +2011-01-19T06:31:50.000000000Z diff --git a/tests/Baseline/hilti.types.tuple.assignment-coerce/output b/tests/Baseline/hilti.types.tuple.assignment-coerce/output new file mode 100644 index 000000000..4989a1dc7 --- /dev/null +++ b/tests/Baseline/hilti.types.tuple.assignment-coerce/output @@ -0,0 +1,2 @@ +(>, [], (not set)) +("foo", [], True) diff --git a/tests/Baseline/hilti.types.tuple.ctor/output b/tests/Baseline/hilti.types.tuple.ctor/output new file mode 100644 index 000000000..56d86ee3e --- /dev/null +++ b/tests/Baseline/hilti.types.tuple.ctor/output @@ -0,0 +1,4 @@ +(1, 2, (3, 4)) +() +("Hello!", True) +Hello!, True diff --git a/tests/Baseline/hilti.types.tuple.index/output b/tests/Baseline/hilti.types.tuple.index/output new file mode 100644 index 000000000..b02f6e249 --- /dev/null +++ b/tests/Baseline/hilti.types.tuple.index/output @@ -0,0 +1,2 @@ +Hello! +True diff --git a/tests/Baseline/hilti.types.union.ops/output b/tests/Baseline/hilti.types.union.ops/output new file mode 100644 index 000000000..2460d5f04 --- /dev/null +++ b/tests/Baseline/hilti.types.union.ops/output @@ -0,0 +1,3 @@ + +$i=42 +$test="X" diff --git a/tests/Baseline/hilti.types.union.same-type/output b/tests/Baseline/hilti.types.union.same-type/output new file mode 100644 index 000000000..6f23d41a8 --- /dev/null +++ b/tests/Baseline/hilti.types.union.same-type/output @@ -0,0 +1,2 @@ +$a="A" +$b="B" diff --git a/tests/Baseline/hilti.types.vector.ctor/output b/tests/Baseline/hilti.types.vector.ctor/output new file mode 100644 index 000000000..9cd01067a --- /dev/null +++ b/tests/Baseline/hilti.types.vector.ctor/output @@ -0,0 +1,11 @@ +[] +[1] +[1, 2, 3] +["s1", "s2"] +[[1, 2], [3, 4], [4, 5]] +[[1, 2], [3, 4], [4, 5]] +[1] +[] +[1] +["s1", "s2"] +[[1, 2], [3, 4], [4, 5]] diff --git a/tests/Baseline/hilti.types.vector.default-values/output b/tests/Baseline/hilti.types.vector.default-values/output new file mode 100644 index 000000000..ea90bd865 --- /dev/null +++ b/tests/Baseline/hilti.types.vector.default-values/output @@ -0,0 +1,2 @@ +X::Undef +X::A1 diff --git a/tests/Baseline/hilti.types.vector.iterator/output b/tests/Baseline/hilti.types.vector.iterator/output new file mode 100644 index 000000000..1f9687ad3 --- /dev/null +++ b/tests/Baseline/hilti.types.vector.iterator/output @@ -0,0 +1,3 @@ + + + diff --git a/tests/Baseline/hilti.types.vector.ops/output b/tests/Baseline/hilti.types.vector.ops/output new file mode 100644 index 000000000..dbeb833ba --- /dev/null +++ b/tests/Baseline/hilti.types.vector.ops/output @@ -0,0 +1,3 @@ +["a", "b", "c", "", "", "e", "f", "g"] +[0, 0, 42, 0, 1] +[1, 0, 3] diff --git a/tests/Baseline/hilti.use-cases.parse-bytes/output b/tests/Baseline/hilti.use-cases.parse-bytes/output new file mode 100644 index 000000000..b9ac0ad08 --- /dev/null +++ b/tests/Baseline/hilti.use-cases.parse-bytes/output @@ -0,0 +1,2 @@ +Parsed: [$a=1, $b=2, $c=1027] +Data left: diff --git a/tests/Baseline/hilti.validation.const-violations/output b/tests/Baseline/hilti.validation.const-violations/output new file mode 100644 index 000000000..e6f82b038 --- /dev/null +++ b/tests/Baseline/hilti.validation.const-violations/output @@ -0,0 +1,11 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:22: cannot resolve operator: >>.push_back(>) +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:23: cannot assign to expression: v = v2 +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:24: cannot assign to expression: v[5] = 1 +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:25: cannot assign to expression: (x, v) = ("X", v) +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:26: call does not match any function: p2(>>) +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:26: candidate functions: +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:26: - p2(>>) +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:29: call does not match any method: .x(>>) +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:29: candidate methods: +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.const-violations/const-violations.hlt:29: - .x(>>) +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.validation.duplicate-2/output b/tests/Baseline/hilti.validation.duplicate-2/output new file mode 100644 index 000000000..971ca707f --- /dev/null +++ b/tests/Baseline/hilti.validation.duplicate-2/output @@ -0,0 +1,2 @@ +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.validation.duplicate-2/duplicate.hlt:5-6: redefinition of 'f' defined in /Users/bbannier/src/spicy/tests/.tmp/hilti.validation.duplicate-2/duplicate.hlt:2-5 +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.validation.duplicate/output b/tests/Baseline/hilti.validation.duplicate/output new file mode 100644 index 000000000..781390e9b --- /dev/null +++ b/tests/Baseline/hilti.validation.duplicate/output @@ -0,0 +1,2 @@ +[error] /Users/bbannier/src/spicy/tests/.tmp/hilti.validation.duplicate/duplicate.hlt:15-16: redefinition of 'i' defined in /Users/bbannier/src/spicy/tests/.tmp/hilti.validation.duplicate/duplicate.hlt:12-15 +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.validation.wildcard-types/output b/tests/Baseline/hilti.validation.wildcard-types/output new file mode 100644 index 000000000..29a938be5 --- /dev/null +++ b/tests/Baseline/hilti.validation.wildcard-types/output @@ -0,0 +1,4 @@ +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.wildcard-types/wildcard-types.hlt:4-6: cannot use wildcard type for variables +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.wildcard-types/wildcard-types.hlt:8: parameter 't' cannot have wildcard type; only allowed with runtime library functions declared with &cxxname +[error] /home/robin/work/spicy/tests/.tmp/hilti.validation.wildcard-types/wildcard-types.hlt:9: cannot use wildcard type for variables +[error] hiltic: aborting after errors diff --git a/tests/Baseline/spicy.doc.debug-hooks/output b/tests/Baseline/spicy.doc.debug-hooks/output new file mode 100644 index 000000000..2a3fe2fc4 --- /dev/null +++ b/tests/Baseline/spicy.doc.debug-hooks/output @@ -0,0 +1,2 @@ +1234 +567890 diff --git a/tests/Baseline/spicy.doc.hello/output b/tests/Baseline/spicy.doc.hello/output new file mode 100644 index 000000000..af5626b4a --- /dev/null +++ b/tests/Baseline/spicy.doc.hello/output @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/Baseline/spicy.doc.my-http/output b/tests/Baseline/spicy.doc.my-http/output new file mode 100644 index 000000000..67c718d58 --- /dev/null +++ b/tests/Baseline/spicy.doc.my-http/output @@ -0,0 +1 @@ +GET, /index.html, 1.0 diff --git a/tests/Baseline/spicy.expressions.list-comprehension/output b/tests/Baseline/spicy.expressions.list-comprehension/output new file mode 100644 index 000000000..7300baa3a --- /dev/null +++ b/tests/Baseline/spicy.expressions.list-comprehension/output @@ -0,0 +1 @@ +[2, 4, 6] diff --git a/tests/Baseline/spicy.lib.protocols.dns.single/output b/tests/Baseline/spicy.lib.protocols.dns.single/output new file mode 100644 index 000000000..c7fec7a92 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.dns.single/output @@ -0,0 +1,9 @@ +[$id=13928, $flags=(1, 0, 0, 0, 1, 1, 0, 0), $qdcount=1, $ancount=2, $nscount=2, $arcount=0, $rejected=False] + +[[$qname=[$label=b".www.joelonsoftware.com"], $qtype=1, $qclass=1]] + +[[$name=[$label=b".www.joelonsoftware.com"], $ty=RDType::CNAME, $class_=1, $ttl=2187.000000s, $rdlen=2, $rname=[$label=b".joelonsoftware.com"], $a=(not set), $mx=(not set), $soa=(not set), $txt=(not set), $rdata=(not set)], [$name=[$label=b".joelonsoftware.com"], $ty=RDType::A, $class_=1, $ttl=2188.000000s, $rdlen=4, $rname=(not set), $a=64.34.80.173, $mx=(not set), $soa=(not set), $txt=(not set), $rdata=(not set)]] + +[[$name=[$label=b".joelonsoftware.com"], $ty=RDType::NS, $class_=1, $ttl=2188.000000s, $rdlen=16, $rname=[$label=b".dns3.fogcreek.com"], $a=(not set), $mx=(not set), $soa=(not set), $txt=(not set), $rdata=(not set)], [$name=[$label=b".joelonsoftware.com"], $ty=RDType::NS, $class_=1, $ttl=2188.000000s, $rdlen=7, $rname=[$label=b".dns5.fogcreek.com"], $a=(not set), $mx=(not set), $soa=(not set), $txt=(not set), $rdata=(not set)]] + +[] diff --git a/tests/Baseline/spicy.lib.protocols.http.gc-release-chunked/gc-leaks b/tests/Baseline/spicy.lib.protocols.http.gc-release-chunked/gc-leaks new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.gc-release-chunked/gc-leaks @@ -0,0 +1 @@ +8 diff --git a/tests/Baseline/spicy.lib.protocols.http.gc-release-sink/gc-leaks b/tests/Baseline/spicy.lib.protocols.http.gc-release-sink/gc-leaks new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.gc-release-sink/gc-leaks @@ -0,0 +1 @@ +8 diff --git a/tests/Baseline/spicy.lib.protocols.http.gc-release-sink/size b/tests/Baseline/spicy.lib.protocols.http.gc-release-sink/size new file mode 100644 index 000000000..1084b795d --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.gc-release-sink/size @@ -0,0 +1 @@ +SIZE 102400 diff --git a/tests/Baseline/spicy.lib.protocols.http.gc-release/gc-leaks b/tests/Baseline/spicy.lib.protocols.http.gc-release/gc-leaks new file mode 100644 index 000000000..7ed6ff82d --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.gc-release/gc-leaks @@ -0,0 +1 @@ +5 diff --git a/tests/Baseline/spicy.lib.protocols.http.gc-release/output b/tests/Baseline/spicy.lib.protocols.http.gc-release/output new file mode 100644 index 000000000..f17d586d9 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.gc-release/output @@ -0,0 +1 @@ +Content-Length:, 1024000 diff --git a/tests/Baseline/spicy.lib.protocols.http.reply-chunked-trailer/output b/tests/Baseline/spicy.lib.protocols.http.reply-chunked-trailer/output new file mode 100644 index 000000000..1015c0984 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.reply-chunked-trailer/output @@ -0,0 +1,2 @@ +I ate it all:, This is the data in the first chunk\r\nand this is the second one +[$reply=[$version=[$number=b"1.1"], $status=200, $reason=b"OK"], $message=[$headers=[[$name=b"DATE", $content=b"Sat, 19 Jun 2010 05:36:25 GMT"], [$name=b"SERVER", $content=b"Apache/2.2.14 (Fedora)"], [$name=b"LAST-MODIFIED", $content=b"Fri, 21 May 2010 16:43:29 GMT"], [$name=b"ETAG", $content=b"\"880084-e0b-4871d6409ae40\""], [$name=b"ACCEPT-RANGES", $content=b"bytes"], [$name=b"CONTENT-LENGTH", $content=b"65"], [$name=b"CONNECTION", $content=b"close"], [$name=b"CONTENT-TYPE", $content=b"text/plain"], [$name=b"TRANSFER-ENCODING", $content=b"chunked"], [$name=b"FOO", $content=b"1111"], [$name=b"BAR", $content=b"2222"]], $end_of_hdr=b"\r\n", $body=[$data=], $has_body=True, $is_request=False, $content_encoding=(not set), $use_content_length=True, $body_len=63, $content_length=65, $content_type=(b"TEXT", b"PLAIN"), $content_type_parameter=b"", $delivery_mode=DeliveryMode::Chunked, $multipart_boundary=(not set), $transfer_encoding=b"chunked"]] diff --git a/tests/Baseline/spicy.lib.protocols.http.reply-chunked/output b/tests/Baseline/spicy.lib.protocols.http.reply-chunked/output new file mode 100644 index 000000000..8bf70e553 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.reply-chunked/output @@ -0,0 +1,2 @@ +I ate it all:, This is the data in the first chunk\r\nand this is the second one +[$reply=[$version=[$number=b"1.1"], $status=200, $reason=b"OK"], $message=[$headers=[[$name=b"DATE", $content=b"Sat, 19 Jun 2010 05:36:25 GMT"], [$name=b"SERVER", $content=b"Apache/2.2.14 (Fedora)"], [$name=b"LAST-MODIFIED", $content=b"Fri, 21 May 2010 16:43:29 GMT"], [$name=b"ETAG", $content=b"\"880084-e0b-4871d6409ae40\""], [$name=b"ACCEPT-RANGES", $content=b"bytes"], [$name=b"CONTENT-LENGTH", $content=b"65"], [$name=b"CONNECTION", $content=b"close"], [$name=b"CONTENT-TYPE", $content=b"text/plain"], [$name=b"TRANSFER-ENCODING", $content=b"chunked"]], $end_of_hdr=b"\r\n", $body=[$data=], $has_body=True, $is_request=False, $content_encoding=(not set), $use_content_length=True, $body_len=63, $content_length=65, $content_type=(b"TEXT", b"PLAIN"), $content_type_parameter=b"", $delivery_mode=DeliveryMode::Chunked, $multipart_boundary=(not set), $transfer_encoding=b"chunked"]] diff --git a/tests/Baseline/spicy.lib.protocols.http.reply-content-length/output b/tests/Baseline/spicy.lib.protocols.http.reply-content-length/output new file mode 100644 index 000000000..740bb81f4 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.reply-content-length/output @@ -0,0 +1,2 @@ +I ate it all:, 1\r\n22\r\n333\r\n4444\r\n55555\r\n666666\r\n7777777\r\n88888888\r\n999999999\r\n0000000000\r\n +[$reply=[$version=[$number=b"1.1"], $status=200, $reason=b"OK"], $message=[$headers=[[$name=b"DATE", $content=b"Sat, 19 Jun 2010 05:36:25 GMT"], [$name=b"SERVER", $content=b"Apache/2.2.14 (Fedora)"], [$name=b"LAST-MODIFIED", $content=b"Fri, 21 May 2010 16:43:29 GMT"], [$name=b"ETAG", $content=b"\"880084-e0b-4871d6409ae40\""], [$name=b"ACCEPT-RANGES", $content=b"bytes"], [$name=b"CONTENT-LENGTH", $content=b"75"], [$name=b"CONNECTION", $content=b"close"], [$name=b"CONTENT-TYPE", $content=b"text/html; charset=UTF-8"]], $end_of_hdr=b"\r\n", $body=[$data=], $has_body=True, $is_request=False, $content_encoding=(not set), $use_content_length=True, $body_len=75, $content_length=75, $content_type=(b"TEXT", b"HTML"), $content_type_parameter=b"charset=UTF-8", $delivery_mode=DeliveryMode::Length, $multipart_boundary=(not set), $transfer_encoding=(not set)]] diff --git a/tests/Baseline/spicy.lib.protocols.http.reply-eod/output b/tests/Baseline/spicy.lib.protocols.http.reply-eod/output new file mode 100644 index 000000000..98f2f1b3e --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.reply-eod/output @@ -0,0 +1,2 @@ +I ate it all:, 1\r\n22\r\n333\r\n4444\r\n55555\r\n666666\r\n7777777\r\n88888888\r\n999999999\r\n0000000000\r\n +[$reply=[$version=[$number=b"1.1"], $status=200, $reason=b"OK"], $message=[$headers=[[$name=b"DATE", $content=b"Sat, 19 Jun 2010 05:36:25 GMT"], [$name=b"SERVER", $content=b"Apache/2.2.14 (Fedora)"], [$name=b"LAST-MODIFIED", $content=b"Fri, 21 May 2010 16:43:29 GMT"], [$name=b"ETAG", $content=b"\"880084-e0b-4871d6409ae40\""], [$name=b"ACCEPT-RANGES", $content=b"bytes"], [$name=b"CONNECTION", $content=b"close"], [$name=b"CONTENT-TYPE", $content=b"text/html; charset=UTF-8"]], $end_of_hdr=b"\r\n", $body=[$data=], $has_body=True, $is_request=False, $content_encoding=(not set), $use_content_length=True, $body_len=75, $content_length=(not set), $content_type=(b"TEXT", b"HTML"), $content_type_parameter=b"charset=UTF-8", $delivery_mode=DeliveryMode::EndOfData, $multipart_boundary=(not set), $transfer_encoding=(not set)]] diff --git a/tests/Baseline/spicy.lib.protocols.http.reply-multipart/output b/tests/Baseline/spicy.lib.protocols.http.reply-multipart/output new file mode 100644 index 000000000..4c6c7b3c8 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.reply-multipart/output @@ -0,0 +1,2 @@ +I ate it all:, This is a message with multiple parts in MIME format.\r\n--frontier\r\nContent-Type: text/plain\r\n\r\nThis is the body of the message.\r\n--frontier\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: base64\r\n\r\nPGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg\r\nYm9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg==\r\n +[$reply=[$version=[$number=b"1.1"], $status=200, $reason=b"OK"], $message=[$headers=[[$name=b"DATE", $content=b"Sat, 19 Jun 2010 05:36:25 GMT"], [$name=b"SERVER", $content=b"Apache/2.2.14 (Fedora)"], [$name=b"LAST-MODIFIED", $content=b"Fri, 21 May 2010 16:43:29 GMT"], [$name=b"ETAG", $content=b"\"880084-e0b-4871d6409ae40\""], [$name=b"ACCEPT-RANGES", $content=b"bytes"], [$name=b"CONNECTION", $content=b"close"], [$name=b"CONTENT-TYPE", $content=b"multipart/mixed; boundary=\"frontier\""]], $end_of_hdr=b"\r\n", $body=[$data=], $has_body=True, $is_request=False, $content_encoding=(not set), $use_content_length=True, $body_len=354, $content_length=(not set), $content_type=(b"MULTIPART", b"MIXED"), $content_type_parameter=b"boundary=\"frontier\"", $delivery_mode=DeliveryMode::Multipart, $multipart_boundary=b"--frontier--\r\n", $transfer_encoding=(not set)]] diff --git a/tests/Baseline/spicy.lib.protocols.http.requests/output b/tests/Baseline/spicy.lib.protocols.http.requests/output new file mode 100644 index 000000000..7a99be6a4 --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.requests/output @@ -0,0 +1,2 @@ +[$request=[$method=b"GET", $uri=b"/a", $version=[$number=b"1.1"]], $message=[$headers=[[$name=b"USER-AGENT", $content=b"curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"], [$name=b"HOST", $content=b"www.icir.org"], [$name=b"ACCEPT", $content=b"*/*"]], $end_of_hdr=b"\r\n", $body=(not set), $has_body=False, $is_request=True, $content_encoding=(not set), $use_content_length=True, $body_len=(not set), $content_length=(not set), $content_type=(b"TEXT", b"PLAIN"), $content_type_parameter=(not set), $delivery_mode=DeliveryMode::EndOfData, $multipart_boundary=(not set), $transfer_encoding=(not set)]] +[$request=[$method=b"GET", $uri=b"/b", $version=[$number=b"1.1"]], $message=[$headers=[[$name=b"USER-AGENT", $content=b"Wuergs/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"], [$name=b"HOST", $content=b"www.aciri.org"], [$name=b"ACCEPT", $content=b"*/*"]], $end_of_hdr=b"\r\n", $body=(not set), $has_body=False, $is_request=True, $content_encoding=(not set), $use_content_length=True, $body_len=(not set), $content_length=(not set), $content_type=(b"TEXT", b"PLAIN"), $content_type_parameter=(not set), $delivery_mode=DeliveryMode::EndOfData, $multipart_boundary=(not set), $transfer_encoding=(not set)]] diff --git a/tests/Baseline/spicy.lib.protocols.http.requests/spicy.lib.protocols.http.request/output b/tests/Baseline/spicy.lib.protocols.http.requests/spicy.lib.protocols.http.request/output new file mode 100644 index 000000000..cffc9286d --- /dev/null +++ b/tests/Baseline/spicy.lib.protocols.http.requests/spicy.lib.protocols.http.request/output @@ -0,0 +1 @@ +[$request=[$method=b"GET", $uri=b"/", $version=[$number=b"1.1"]], $message=[$headers=[[$name=b"USER-AGENT", $content=b"curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"], [$name=b"HOST", $content=b"www.icir.org"], [$name=b"ACCEPT", $content=b"*/*"]], $end_of_hdr=b"\r\n", $body=(not set), $has_body=False, $is_request=True, $content_encoding=(not set), $use_content_length=True, $body_len=(not set), $content_length=(not set), $content_type=(b"TEXT", b"PLAIN"), $content_type_parameter=(not set), $delivery_mode=DeliveryMode::EndOfData, $multipart_boundary=(not set), $transfer_encoding=(not set)]] diff --git a/tests/Baseline/spicy.rt.base64-filter/output b/tests/Baseline/spicy.rt.base64-filter/output new file mode 100644 index 000000000..fa232a13a --- /dev/null +++ b/tests/Baseline/spicy.rt.base64-filter/output @@ -0,0 +1,4 @@ +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!\n"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!\n"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!\n"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!\n"] diff --git a/tests/Baseline/spicy.rt.base64/output b/tests/Baseline/spicy.rt.base64/output new file mode 100644 index 000000000..318e09579 --- /dev/null +++ b/tests/Baseline/spicy.rt.base64/output @@ -0,0 +1,2 @@ +MTIzNDU2Nzg5MA== +1234567890 diff --git a/tests/Baseline/spicy.rt.load-hlto/output b/tests/Baseline/spicy.rt.load-hlto/output new file mode 100644 index 000000000..86ea0509e --- /dev/null +++ b/tests/Baseline/spicy.rt.load-hlto/output @@ -0,0 +1,2 @@ +Hello, world! +[error] spicy-driver: no parsers available diff --git a/tests/Baseline/spicy.rt.print-binary/output b/tests/Baseline/spicy.rt.print-binary/output new file mode 100644 index 000000000..4d4c78fd6 --- /dev/null +++ b/tests/Baseline/spicy.rt.print-binary/output @@ -0,0 +1,3 @@ +H\xc3\xbcrz +X\r\nY +H\xc3\xbcrz, X\r\nY diff --git a/tests/Baseline/spicy.rt.release-vs-debug/output b/tests/Baseline/spicy.rt.release-vs-debug/output new file mode 100644 index 000000000..b4dc76e4a --- /dev/null +++ b/tests/Baseline/spicy.rt.release-vs-debug/output @@ -0,0 +1,6 @@ +== spicy-build +HILTI runtime library version X.X.X [release build] +Spicy runtime library version X.X.X [release build] +== spicy-build -d +HILTI runtime library version X.X.X [debug build] +Spicy runtime library version X.X.X [debug build] diff --git a/tests/Baseline/spicy.rt.time/output b/tests/Baseline/spicy.rt.time/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.rt.zlib-filter/output b/tests/Baseline/spicy.rt.zlib-filter/output new file mode 100644 index 000000000..25b138d7f --- /dev/null +++ b/tests/Baseline/spicy.rt.zlib-filter/output @@ -0,0 +1,4 @@ +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!"] diff --git a/tests/Baseline/spicy.rt.zlib/output b/tests/Baseline/spicy.rt.zlib/output new file mode 100644 index 000000000..2e6e1956b --- /dev/null +++ b/tests/Baseline/spicy.rt.zlib/output @@ -0,0 +1 @@ +Hello, Spicy! diff --git a/tests/Baseline/spicy.statements.assert/output b/tests/Baseline/spicy.statements.assert/output new file mode 100644 index 000000000..5a1e62fab --- /dev/null +++ b/tests/Baseline/spicy.statements.assert/output @@ -0,0 +1,2 @@ +uncaught exception hilti::rt::AssertionFailure: failed expression '1 == 0' (fail.spicy:3) +uncaught exception hilti::rt::AssertionFailure: my error (fail2.spicy:3) diff --git a/tests/Baseline/spicy.statements.for/output b/tests/Baseline/spicy.statements.for/output new file mode 100644 index 000000000..5fdde9fae --- /dev/null +++ b/tests/Baseline/spicy.statements.for/output @@ -0,0 +1,9 @@ +1 +2 +3 +1 +2 +3 +1 +3 +4 diff --git a/tests/Baseline/spicy.statements.print/output b/tests/Baseline/spicy.statements.print/output new file mode 100644 index 000000000..3e352429a --- /dev/null +++ b/tests/Baseline/spicy.statements.print/output @@ -0,0 +1,5 @@ +True +True, 32 + +Xyz +(1, 2, True, b"XX") diff --git a/tests/Baseline/spicy.statements.switch/output b/tests/Baseline/spicy.statements.switch/output new file mode 100644 index 000000000..7386d9f25 --- /dev/null +++ b/tests/Baseline/spicy.statements.switch/output @@ -0,0 +1 @@ +uncaught exception hilti::rt::UnhandledSwitchCase: 3 (fail.spicy:5-8) diff --git a/tests/Baseline/spicy.statements.while/output b/tests/Baseline/spicy.statements.while/output new file mode 100644 index 000000000..5ef5d2d17 --- /dev/null +++ b/tests/Baseline/spicy.statements.while/output @@ -0,0 +1,25 @@ +-- A +1 +2 +3 +4 +5 +-- B +3 +4 +5 +-- C +1 +3 +4 +-- D +1 +2 +3 +4 +5 +else +-- E +1 +2 +3 diff --git a/tests/Baseline/spicy.tools.spicy-build-driver/no-main.output b/tests/Baseline/spicy.tools.spicy-build-driver/no-main.output new file mode 100644 index 000000000..b0eb1a0ca --- /dev/null +++ b/tests/Baseline/spicy.tools.spicy-build-driver/no-main.output @@ -0,0 +1 @@ +./a.out: This is a default entry point provided by the HILTI runtime library that initializes any available modules. diff --git a/tests/Baseline/spicy.tools.spicy-build-driver/output b/tests/Baseline/spicy.tools.spicy-build-driver/output new file mode 100644 index 000000000..c3b535eb3 --- /dev/null +++ b/tests/Baseline/spicy.tools.spicy-build-driver/output @@ -0,0 +1,4 @@ +[$f1=(not set), $f2=(not set)] +10000 +42 +[$f1=10000, $f2=42] diff --git a/tests/Baseline/spicy.tools.spicy-build-hello-world/output b/tests/Baseline/spicy.tools.spicy-build-hello-world/output new file mode 100644 index 000000000..95e75d98d --- /dev/null +++ b/tests/Baseline/spicy.tools.spicy-build-hello-world/output @@ -0,0 +1,2 @@ +Hello, world! +Hello, world! diff --git a/tests/Baseline/spicy.tools.spicy-driver-library-path/output b/tests/Baseline/spicy.tools.spicy-driver-library-path/output new file mode 100644 index 000000000..8be952d3b --- /dev/null +++ b/tests/Baseline/spicy.tools.spicy-driver-library-path/output @@ -0,0 +1,6 @@ +[$i=1] +[$i=1] +[$i=1] +[error] test.spicy:4: cannot import module 'Bar': cannot find file +[error] spicy-driver: aborting after errors +[$i=1] diff --git a/tests/Baseline/spicy.tools.spicyc-hello-world/output b/tests/Baseline/spicy.tools.spicyc-hello-world/output new file mode 100644 index 000000000..95e75d98d --- /dev/null +++ b/tests/Baseline/spicy.tools.spicyc-hello-world/output @@ -0,0 +1,2 @@ +Hello, world! +Hello, world! diff --git a/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt b/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt new file mode 100644 index 000000000..72249ad62 --- /dev/null +++ b/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt @@ -0,0 +1,26 @@ +// Begin of Foo (from "/home/robin/work/spicy/tests/.tmp/spicy.tools.spicyc-hello-world/spicyc-hello-world.spicy") +// Compiled by HILTI version 0.2.0-dev + +#include + +#include +#include + +namespace __hlt::Foo { + extern void __init_module(); + extern void __register_module(); +} + +HILTI_PRE_INIT(__hlt::Foo::__register_module) + +extern void __hlt::Foo::__init_module() { + hilti::rt::print(std::string("Hello, world!"), true); + hilti::rt::printValues(std::make_tuple(std::string("Hello"), std::string("world!")), true); +} + +extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, nullptr, nullptr}); } + +/* __HILTI_LINKER_V1__ +{"module":"Foo","namespace":"__hlt::Foo","path":"/home/robin/work/spicy/tests/.tmp/spicy.tools.spicyc-hello-world/spicyc-hello-world.spicy","version":1} +*/ + diff --git a/tests/Baseline/spicy.tools.spicyc-precompiled/output b/tests/Baseline/spicy.tools.spicyc-precompiled/output new file mode 100644 index 000000000..758c1f86b --- /dev/null +++ b/tests/Baseline/spicy.tools.spicyc-precompiled/output @@ -0,0 +1,4 @@ +[$f1=(not set), $f2=(not set)] +1 +2 +[$f1=1, $f2=2] diff --git a/tests/Baseline/spicy.types.address.ops/output b/tests/Baseline/spicy.types.address.ops/output new file mode 100644 index 000000000..197553aea --- /dev/null +++ b/tests/Baseline/spicy.types.address.ops/output @@ -0,0 +1,2 @@ +1.2.3.4 +2001:db8:85a3:8d3:1319:8a2e:370:7348 diff --git a/tests/Baseline/spicy.types.address.parse/output b/tests/Baseline/spicy.types.address.parse/output new file mode 100644 index 000000000..7608d201e --- /dev/null +++ b/tests/Baseline/spicy.types.address.parse/output @@ -0,0 +1,3 @@ +1.2.3.4 +4.3.2.1 +102:304:506:708:910:1112:1314:1516 diff --git a/tests/Baseline/spicy.types.bitfield.parse-bitorder/output b/tests/Baseline/spicy.types.bitfield.parse-bitorder/output new file mode 100644 index 000000000..f089a9fb7 --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.parse-bitorder/output @@ -0,0 +1,3 @@ +0, 2 +1, 0 +1, 0 diff --git a/tests/Baseline/spicy.types.bitfield.parse-debug-output/output b/tests/Baseline/spicy.types.bitfield.parse-debug-output/output new file mode 100644 index 000000000..ab51e11c8 --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.parse-debug-output/output @@ -0,0 +1,5 @@ +[spicy] Mini::test +[spicy] a = 10 +[spicy] b = 63 +[spicy] x1 = 15 +[spicy] x2 = Foo::A diff --git a/tests/Baseline/spicy.types.bitfield.parse-enum/output b/tests/Baseline/spicy.types.bitfield.parse-enum/output new file mode 100644 index 000000000..9bafc1c3b --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.parse-enum/output @@ -0,0 +1,2 @@ +Foo::A +Foo:: diff --git a/tests/Baseline/spicy.types.bitfield.parse/output b/tests/Baseline/spicy.types.bitfield.parse/output new file mode 100644 index 000000000..7e0aada66 --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.parse/output @@ -0,0 +1,4 @@ +1 +3 +3 +[$f=(1, 3, 3)] diff --git a/tests/Baseline/spicy.types.bytes.attr-validity/output b/tests/Baseline/spicy.types.bytes.attr-validity/output new file mode 100644 index 000000000..d11695608 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.attr-validity/output @@ -0,0 +1,4 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.bytes.attr-validity/attr-validity.spicy:7: bytes field requires one of &size, &eod, or &until +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.bytes.attr-validity/attr-validity.spicy:8: &until must provide an expression +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.bytes.attr-validity/attr-validity.spicy:10: &eod incompatible with &until +[error] spicy-driver: aborting after errors diff --git a/tests/Baseline/spicy.types.bytes.iterator/output b/tests/Baseline/spicy.types.bytes.iterator/output new file mode 100644 index 000000000..3c15c7876 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.iterator/output @@ -0,0 +1,3 @@ +49 +50 +51 diff --git a/tests/Baseline/spicy.types.bytes.join/output b/tests/Baseline/spicy.types.bytes.join/output new file mode 100644 index 000000000..706eeff07 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.join/output @@ -0,0 +1 @@ +a.b.c diff --git a/tests/Baseline/spicy.types.bytes.parse-chunked-error/output b/tests/Baseline/spicy.types.bytes.parse-chunked-error/output new file mode 100644 index 000000000..9ef3b1c86 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-chunked-error/output @@ -0,0 +1,3 @@ +1234567 +890 +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: insufficient input for &size (/home/robin/work/spicy/tests/.tmp/spicy.types.bytes.parse-chunked-error/parse-chunked-error.spicy:9) diff --git a/tests/Baseline/spicy.types.bytes.parse-chunked/output b/tests/Baseline/spicy.types.bytes.parse-chunked/output new file mode 100644 index 000000000..bd63dde78 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-chunked/output @@ -0,0 +1,16 @@ +1234567890 +[$b1=b"abc", $b2=b"1234567890"] +123 +456 +789 +0 +[$b1=b"abc", $b2=b"0"] +1234567 +890 +[$c1=b"abc", $c2=b"1234567", $c3=(not set), $c4=b"890"] +123 +456 +7 +89 +0 +[$c1=b"abc", $c2=b"7", $c3=(not set), $c4=b"0"] diff --git a/tests/Baseline/spicy.types.bytes.parse-ctor-error/output b/tests/Baseline/spicy.types.bytes.parse-ctor-error/output new file mode 100644 index 000000000..13d619458 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-ctor-error/output @@ -0,0 +1,2 @@ +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: expecting 'def' (/home/robin/work/spicy/tests/.tmp/spicy.types.bytes.parse-ctor-error/parse-ctor-error.spicy:10) +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: expecting 'def' (/home/robin/work/spicy/tests/.tmp/spicy.types.bytes.parse-ctor-error/parse-ctor-error.spicy:10) diff --git a/tests/Baseline/spicy.types.bytes.parse-ctor/output b/tests/Baseline/spicy.types.bytes.parse-ctor/output new file mode 100644 index 000000000..ce6bd3f77 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-ctor/output @@ -0,0 +1 @@ +[$b1=b"abc", $b2=b"def"] diff --git a/tests/Baseline/spicy.types.bytes.parse-eod/output b/tests/Baseline/spicy.types.bytes.parse-eod/output new file mode 100644 index 000000000..ce6bd3f77 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-eod/output @@ -0,0 +1 @@ +[$b1=b"abc", $b2=b"def"] diff --git a/tests/Baseline/spicy.types.bytes.parse-inc/output b/tests/Baseline/spicy.types.bytes.parse-inc/output new file mode 100644 index 000000000..9366c423e --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-inc/output @@ -0,0 +1 @@ +[$a=b"lllll", $b=b"aa", $uuu=b"1234", $remaining=b"rrrrrrr"] diff --git a/tests/Baseline/spicy.types.bytes.parse-length-coercion-error/output b/tests/Baseline/spicy.types.bytes.parse-length-coercion-error/output new file mode 100644 index 000000000..8c15b227d --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-length-coercion-error/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.bytes.parse-length-coercion-error/parse-length-coercion-error.spicy:8: cannot coerce expression 'b"5"' to type 'uint<64>' +[error] spicy-driver: aborting after errors diff --git a/tests/Baseline/spicy.types.bytes.parse-length-coercion/output b/tests/Baseline/spicy.types.bytes.parse-length-coercion/output new file mode 100644 index 000000000..9d376c354 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-length-coercion/output @@ -0,0 +1 @@ +abc, 12345, def diff --git a/tests/Baseline/spicy.types.bytes.parse-length-eod-chunked/output b/tests/Baseline/spicy.types.bytes.parse-length-eod-chunked/output new file mode 100644 index 000000000..333bc5479 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-length-eod-chunked/output @@ -0,0 +1,4 @@ +123 +456 +78 +[$b1=b"abc", $b2=b"78"] diff --git a/tests/Baseline/spicy.types.bytes.parse-length-eod/output b/tests/Baseline/spicy.types.bytes.parse-length-eod/output new file mode 100644 index 000000000..1c1cc3973 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-length-eod/output @@ -0,0 +1 @@ +[$b1=b"abc", $b2=b"123"] diff --git a/tests/Baseline/spicy.types.bytes.parse-length-error/output b/tests/Baseline/spicy.types.bytes.parse-length-error/output new file mode 100644 index 000000000..eb78a7a64 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-length-error/output @@ -0,0 +1 @@ +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: insufficient input for &size (/home/robin/work/spicy/tests/.tmp/spicy.types.bytes.parse-length-error/parse-length-error.spicy:8) diff --git a/tests/Baseline/spicy.types.bytes.parse-length/output b/tests/Baseline/spicy.types.bytes.parse-length/output new file mode 100644 index 000000000..46af3e9b0 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-length/output @@ -0,0 +1 @@ +[$b1=b"abc", $b2=b"12345", $b3=b"def"] diff --git a/tests/Baseline/spicy.types.bytes.parse-until-error/output b/tests/Baseline/spicy.types.bytes.parse-until-error/output new file mode 100644 index 000000000..1b9fa7071 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-until-error/output @@ -0,0 +1 @@ +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (/home/robin/work/spicy/tests/.tmp/spicy.types.bytes.parse-until-error/parse-until-error.spicy:8) diff --git a/tests/Baseline/spicy.types.bytes.parse-until/output b/tests/Baseline/spicy.types.bytes.parse-until/output new file mode 100644 index 000000000..f4463a739 --- /dev/null +++ b/tests/Baseline/spicy.types.bytes.parse-until/output @@ -0,0 +1 @@ +[$b1=b"abc", $b2=b"12345"] diff --git a/tests/Baseline/spicy.types.enum.type/output b/tests/Baseline/spicy.types.enum.type/output new file mode 100644 index 000000000..11d63fbe4 --- /dev/null +++ b/tests/Baseline/spicy.types.enum.type/output @@ -0,0 +1,6 @@ +X::A1 +Y::Undef +X::A1 +Y::B2 +Y::Undef +[$x1=X::Undef, $x2=(not set), $y1=(not set)] diff --git a/tests/Baseline/spicy.types.id.validation/output b/tests/Baseline/spicy.types.id.validation/output new file mode 100644 index 000000000..05d4ab588 --- /dev/null +++ b/tests/Baseline/spicy.types.id.validation/output @@ -0,0 +1,6 @@ +[error] /Users/bbannier/src/spicy/tests/.tmp/spicy.types.id.validation/validation.spicy:6: Invalid ID 'a-b': cannot contain '-' +[error] /Users/bbannier/src/spicy/tests/.tmp/spicy.types.id.validation/validation.spicy:7: Invalid ID '__private_name': cannot start with '__' +[error] /Users/bbannier/src/spicy/tests/.tmp/spicy.types.id.validation/validation.spicy:12: Invalid ID 'a-b': cannot contain '-' +[error] /Users/bbannier/src/spicy/tests/.tmp/spicy.types.id.validation/validation.spicy:15: Invalid ID 'Bar-Baz': cannot contain '-' +[error] /Users/bbannier/src/spicy/tests/.tmp/spicy.types.id.validation/validation.spicy:17: Invalid ID 'f-1': cannot contain '-' +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.coercion-fail/output b/tests/Baseline/spicy.types.integer.coercion-fail/output new file mode 100644 index 000000000..dc7c5ab79 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.coercion-fail/output @@ -0,0 +1,17 @@ +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:6-7: cannot coerce expression '256' of type 'uint<64>' to type 'uint<8>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:7-8: cannot coerce expression '-1' of type 'int<64>' to type 'uint<8>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:8-10: cannot coerce expression '128' of type 'uint<64>' to type 'int<8>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:10-11: cannot coerce expression '-129' of type 'int<64>' to type 'int<8>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:11-13: cannot coerce expression '65536' of type 'uint<64>' to type 'uint<16>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:13-14: cannot coerce expression '-1' of type 'int<64>' to type 'uint<16>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:14-16: cannot coerce expression '32768' of type 'uint<64>' to type 'int<16>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:16-17: cannot coerce expression '-32769' of type 'int<64>' to type 'int<16>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:17-19: cannot coerce expression '4294967296' of type 'uint<64>' to type 'uint<32>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:19-20: cannot coerce expression '-1' of type 'int<64>' to type 'uint<32>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:20-22: cannot coerce expression '2147483648' of type 'uint<64>' to type 'int<32>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:22-23: cannot coerce expression '-2147483649' of type 'int<64>' to type 'int<32>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:23-26: cannot coerce expression '-1' of type 'int<64>' to type 'uint<64>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:26-28: cannot coerce expression '9223372036854775808' of type 'uint<64>' to type 'int<64>' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:28-33: cannot coerce expression '9007199254740993' of type 'uint<64>' to type 'real' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.integer.coercion-fail/coercion-fail.spicy:33-34: cannot coerce expression '-9007199254740993' of type 'int<64>' to type 'real' +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-2/output b/tests/Baseline/spicy.types.integer.ctor-fail-2/output new file mode 100644 index 000000000..d3f369a16 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-2/output @@ -0,0 +1,8 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: invalid character +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: invalid character +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: invalid character +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: invalid character +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: invalid character +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: invalid character +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-2/ctor-fail.spicy:1: syntax error, unexpected ')', expecting unsigned integer value +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-3/output b/tests/Baseline/spicy.types.integer.ctor-fail-3/output new file mode 100644 index 000000000..0946e1a45 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-3/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-3/ctor-fail.spicy:1: syntax error, unexpected '-', expecting unsigned integer value +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-4/output b/tests/Baseline/spicy.types.integer.ctor-fail-4/output new file mode 100644 index 000000000..5e1975690 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-4/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-4/ctor-fail.spicy:1: syntax error, unexpected '-', expecting unsigned integer value +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-5/output b/tests/Baseline/spicy.types.integer.ctor-fail-5/output new file mode 100644 index 000000000..c3120e1d2 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-5/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-5/ctor-fail.spicy:1: syntax error, unexpected '-', expecting unsigned integer value +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-6/output b/tests/Baseline/spicy.types.integer.ctor-fail-6/output new file mode 100644 index 000000000..438552f15 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-6/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-6/ctor-fail.spicy:1: syntax error, unexpected '-', expecting unsigned integer value +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail-7/output b/tests/Baseline/spicy.types.integer.ctor-fail-7/output new file mode 100644 index 000000000..e610ca6c7 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail-7/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail-7/ctor-fail.spicy:1: integer literal range error +[error] spicyc: parse error diff --git a/tests/Baseline/spicy.types.integer.ctor-fail/output b/tests/Baseline/spicy.types.integer.ctor-fail/output new file mode 100644 index 000000000..554c84263 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.ctor-fail/output @@ -0,0 +1,10 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:7: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:8: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:9: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:12: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:13: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:14: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:17: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:18: integer value out of range for type +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.ctor-fail/ctor-fail.spicy:19: integer value out of range for type +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output b/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output new file mode 100644 index 000000000..61957764f --- /dev/null +++ b/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.parse-byte-order-from-parameter-fail/parse-byte-order-from-parameter-fail.spicy:11: type mismatch, expression has type 'string', but expected 'spicy::ByteOrder' +[error] spicy-driver: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.parse-lahead/output b/tests/Baseline/spicy.types.integer.parse-lahead/output new file mode 100644 index 000000000..6f7366dc5 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.parse-lahead/output @@ -0,0 +1,4 @@ +[$v=1] +[$v=2] +[$v=3] +[$lines=[[$v=1], [$v=2], [$v=3]], $term=65535] diff --git a/tests/Baseline/spicy.types.integer.wrong-assign/output b/tests/Baseline/spicy.types.integer.wrong-assign/output new file mode 100644 index 000000000..27e6bd2b9 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.wrong-assign/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.wrong-assign/wrong-assign.spicy:9: cannot coerce expression 'Test::a' of type 'int<32>' to type 'uint<32>' +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.wrong-cast/output b/tests/Baseline/spicy.types.integer.wrong-cast/output new file mode 100644 index 000000000..04a2a5904 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.wrong-cast/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.wrong-cast/wrong-cast.spicy:8: cannot resolve operator: cast<>>(>) +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.wrong-int-mix/output b/tests/Baseline/spicy.types.integer.wrong-int-mix/output new file mode 100644 index 000000000..4af150899 --- /dev/null +++ b/tests/Baseline/spicy.types.integer.wrong-int-mix/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.integer.wrong-int-mix/wrong-int-mix.spicy:9: cannot resolve operator: > + > +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.interval.ops/output b/tests/Baseline/spicy.types.interval.ops/output new file mode 100644 index 000000000..d119c4f34 --- /dev/null +++ b/tests/Baseline/spicy.types.interval.ops/output @@ -0,0 +1,2 @@ +2.500000s +1.500000s diff --git a/tests/Baseline/spicy.types.map.ops/output b/tests/Baseline/spicy.types.map.ops/output new file mode 100644 index 000000000..ea3e5dcb9 --- /dev/null +++ b/tests/Baseline/spicy.types.map.ops/output @@ -0,0 +1,2 @@ +{1: AAA, 2: BBB, 3: CCC} +b"BBB" diff --git a/tests/Baseline/spicy.types.network.ops/output b/tests/Baseline/spicy.types.network.ops/output new file mode 100644 index 000000000..541156e9d --- /dev/null +++ b/tests/Baseline/spicy.types.network.ops/output @@ -0,0 +1,3 @@ +2001:db8::/48 +192.168.1.0/24 +192.168.1.0/24 diff --git a/tests/Baseline/spicy.types.optional.ops/output b/tests/Baseline/spicy.types.optional.ops/output new file mode 100644 index 000000000..9fafd0fd3 --- /dev/null +++ b/tests/Baseline/spicy.types.optional.ops/output @@ -0,0 +1,3 @@ +(not set) +123 +456 diff --git a/tests/Baseline/spicy.types.port.ops/output b/tests/Baseline/spicy.types.port.ops/output new file mode 100644 index 000000000..42fb5f4e6 --- /dev/null +++ b/tests/Baseline/spicy.types.port.ops/output @@ -0,0 +1,2 @@ +80/tcp +123/udp diff --git a/tests/Baseline/spicy.types.real.coercion-fail/output b/tests/Baseline/spicy.types.real.coercion-fail/output new file mode 100644 index 000000000..9266bfd2e --- /dev/null +++ b/tests/Baseline/spicy.types.real.coercion-fail/output @@ -0,0 +1,7 @@ +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.real.coercion-fail/coercion-fail.spicy:4-6: cannot coerce expression '0x1.028f5c28f5c29p+0' of type 'real' to type 'bool' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.real.coercion-fail/coercion-fail.spicy:6-7: cannot coerce expression '0x0p+0' of type 'real' to type 'bool' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.real.coercion-fail/coercion-fail.spicy:7-9: cannot coerce expression '18446744073709551615' of type 'uint<64>' to type 'real' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.real.coercion-fail/coercion-fail.spicy:9-11: cannot coerce expression '18446744073709550592' of type 'uint<64>' to type 'real' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.real.coercion-fail/coercion-fail.spicy:11-13: cannot coerce expression '-9223372036854775296' of type 'int<64>' to type 'real' +[error] /home/will/repos/spicy/tests/.tmp/spicy.types.real.coercion-fail/coercion-fail.spicy:13-15: cannot coerce expression '9007199254740993' of type 'uint<64>' to type 'real' +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.real.parse/output b/tests/Baseline/spicy.types.real.parse/output new file mode 100644 index 000000000..5888488ec --- /dev/null +++ b/tests/Baseline/spicy.types.real.parse/output @@ -0,0 +1 @@ +[$f1=12.3457, $f2=12.3457, $f3=3.14, $f4=3.14] diff --git a/tests/Baseline/spicy.types.reference.operators/output b/tests/Baseline/spicy.types.reference.operators/output new file mode 100644 index 000000000..b6615c74a --- /dev/null +++ b/tests/Baseline/spicy.types.reference.operators/output @@ -0,0 +1 @@ +Null diff --git a/tests/Baseline/spicy.types.regexp.operators/output b/tests/Baseline/spicy.types.regexp.operators/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.regexp.parse-ctor/output b/tests/Baseline/spicy.types.regexp.parse-ctor/output new file mode 100644 index 000000000..56d6ca4ba --- /dev/null +++ b/tests/Baseline/spicy.types.regexp.parse-ctor/output @@ -0,0 +1,4 @@ +[$b1=b"abc", $b2=b"def", $b3=b"hij"] +[$b1=b"abc", $b2=b"deeeeef", $b3=b"hij"] +[$b1=b"abc", $b2=b"def", $b3=b"hij"] +[$b1=b"abc", $b2=b"def", $b3=b"hij"] diff --git a/tests/Baseline/spicy.types.set.ops/output b/tests/Baseline/spicy.types.set.ops/output new file mode 100644 index 000000000..139b242d7 --- /dev/null +++ b/tests/Baseline/spicy.types.set.ops/output @@ -0,0 +1,2 @@ +{"A", "B"} +{1, 2, 3} diff --git a/tests/Baseline/spicy.types.sink.connect-mime/output b/tests/Baseline/spicy.types.sink.connect-mime/output new file mode 100644 index 000000000..f31f09126 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.connect-mime/output @@ -0,0 +1,3 @@ +Sub , [$s1=b"34", $s2=b"567abcde"] +Main, [$a=b"12", $b=b"34567", $c=b"890", $data=] +Sub3 , [$s=b"34567abcde"] diff --git a/tests/Baseline/spicy.types.sink.connect/output b/tests/Baseline/spicy.types.sink.connect/output new file mode 100644 index 000000000..2612cbe69 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.connect/output @@ -0,0 +1,3 @@ +Sub , [$s1=b"34", $s2=b"567abcde"] +Main, [$a=b"12", $b=b"34567", $c=b"890", $data=] +Sub2 , foo, [$s=b"34567abcde"] diff --git a/tests/Baseline/spicy.types.sink.filter-it/output b/tests/Baseline/spicy.types.sink.filter-it/output new file mode 100644 index 000000000..2e6e1956b --- /dev/null +++ b/tests/Baseline/spicy.types.sink.filter-it/output @@ -0,0 +1 @@ +Hello, Spicy! diff --git a/tests/Baseline/spicy.types.sink.reassembler.auto-trim/output b/tests/Baseline/spicy.types.sink.reassembler.auto-trim/output new file mode 100644 index 000000000..d07cf5281 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.auto-trim/output @@ -0,0 +1,3 @@ +Overlap at 1: 2 vs X +Overlap at 1: 2 vs Y +123456 diff --git a/tests/Baseline/spicy.types.sink.reassembler.basic/output b/tests/Baseline/spicy.types.sink.reassembler.basic/output new file mode 100644 index 000000000..51ece89df --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.basic/output @@ -0,0 +1,4 @@ +0123 +01234567 +0123456789 +0123456789 diff --git a/tests/Baseline/spicy.types.sink.reassembler.custom-length/output b/tests/Baseline/spicy.types.sink.reassembler.custom-length/output new file mode 100644 index 000000000..4553c8209 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.custom-length/output @@ -0,0 +1 @@ +0000011111222223333344444 diff --git a/tests/Baseline/spicy.types.sink.reassembler.gap/output b/tests/Baseline/spicy.types.sink.reassembler.gap/output new file mode 100644 index 000000000..135442f5d --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.gap/output @@ -0,0 +1,11 @@ +Gap at input position 5, length 3 +01234 + +Gap at input position 5, length 3 +Skipped to position 8 +01234890ABC + +Undelivered data at position 1: 1 +Undelivered data at position 3: 34 +Skipped to position 8 +890ABC diff --git a/tests/Baseline/spicy.types.sink.reassembler.init-seq/output b/tests/Baseline/spicy.types.sink.reassembler.init-seq/output new file mode 100644 index 000000000..51ece89df --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.init-seq/output @@ -0,0 +1,4 @@ +0123 +01234567 +0123456789 +0123456789 diff --git a/tests/Baseline/spicy.types.sink.reassembler.overlap/output b/tests/Baseline/spicy.types.sink.reassembler.overlap/output new file mode 100644 index 000000000..6f42bf7f7 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.overlap/output @@ -0,0 +1,12 @@ +Overlap at 2: 23 vs AB +0123456789 + +Overlap at 1: 123 vs ABC +0123456789 + +Overlap at 1: 123 vs ABC +Overlap at 4: D vs 4 +0123D56789 + +Overlap at 2: 23 vs 2A +0123B56 diff --git a/tests/Baseline/spicy.types.sink.reassembler.policy/output b/tests/Baseline/spicy.types.sink.reassembler.policy/output new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.sink.reassembler.sequence/output b/tests/Baseline/spicy.types.sink.reassembler.sequence/output new file mode 100644 index 000000000..24bac6239 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.sequence/output @@ -0,0 +1,10 @@ +2 +2 +2 +2 +4 +4 +4 +4 +10 +0123456789 diff --git a/tests/Baseline/spicy.types.sink.reassembler.skip/output b/tests/Baseline/spicy.types.sink.reassembler.skip/output new file mode 100644 index 000000000..53ff14834 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.skip/output @@ -0,0 +1,5 @@ +Skipped to position 6 +01236789 + +Skipped to position 6 +016789 diff --git a/tests/Baseline/spicy.types.sink.reassembler.wrong-init-seq/output b/tests/Baseline/spicy.types.sink.reassembler.wrong-init-seq/output new file mode 100644 index 000000000..ef8c917c5 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.reassembler.wrong-init-seq/output @@ -0,0 +1 @@ +[fatal error] terminating with uncaught exception of type spicy::rt::sink::Error: sink cannot update initial sequence number after activity has already been seen diff --git a/tests/Baseline/spicy.types.sink.write/output b/tests/Baseline/spicy.types.sink.write/output new file mode 100644 index 000000000..1c9235c96 --- /dev/null +++ b/tests/Baseline/spicy.types.sink.write/output @@ -0,0 +1,2 @@ +Sub , [$s1=b"12", $s2=b"34567890"] +Main, [$a=b"12", $b=b"34567", $c=b"890", $d=b"abcde", $data=], 10 diff --git a/tests/Baseline/spicy.types.string.operators/output b/tests/Baseline/spicy.types.string.operators/output new file mode 100644 index 000000000..68a0622e1 --- /dev/null +++ b/tests/Baseline/spicy.types.string.operators/output @@ -0,0 +1 @@ +("Spicy", "Spïcy", "Spicy") diff --git a/tests/Baseline/spicy.types.time.ops/output b/tests/Baseline/spicy.types.time.ops/output new file mode 100644 index 000000000..ec020fdff --- /dev/null +++ b/tests/Baseline/spicy.types.time.ops/output @@ -0,0 +1 @@ +2011-01-19T05:31:50.500000000Z diff --git a/tests/Baseline/spicy.types.unit.attr-parse-at/output b/tests/Baseline/spicy.types.unit.attr-parse-at/output new file mode 100644 index 000000000..d4f3bbbf2 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.attr-parse-at/output @@ -0,0 +1 @@ +1234, 5678, 1234567890, 90 diff --git a/tests/Baseline/spicy.types.unit.attr-parse-from-expr/output b/tests/Baseline/spicy.types.unit.attr-parse-from-expr/output new file mode 100644 index 000000000..f05e51a87 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.attr-parse-from-expr/output @@ -0,0 +1 @@ +[$x=b"\x01\x02", $y=258, $z=b"\x03\x04"] diff --git a/tests/Baseline/spicy.types.unit.attr-parse-from/output b/tests/Baseline/spicy.types.unit.attr-parse-from/output new file mode 100644 index 000000000..fd758cd97 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.attr-parse-from/output @@ -0,0 +1 @@ +[$a=b"12345", $B=b"ABCDE", $C=b"FGHI", $d=b"67890"] diff --git a/tests/Baseline/spicy.types.unit.attribute-access/output b/tests/Baseline/spicy.types.unit.attribute-access/output new file mode 100644 index 000000000..ac9453953 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.attribute-access/output @@ -0,0 +1,4 @@ +[$f1=(not set), $f2=(not set), $f3=(not set)] +[$f1=16, $f2=32, $f3=48] +[$f1=(not set), $f2=(not set)] +[fatal error] terminating with uncaught exception of type hilti::rt::AttributeNotSet: struct attribute not set (fail.spicy:6) diff --git a/tests/Baseline/spicy.types.unit.basic-incremental/debug.log b/tests/Baseline/spicy.types.unit.basic-incremental/debug.log new file mode 100644 index 000000000..c57ee8c7d --- /dev/null +++ b/tests/Baseline/spicy.types.unit.basic-incremental/debug.log @@ -0,0 +1,3 @@ +[spicy] Mini::Test +[spicy] f1 = 10000 +[spicy] f2 = 42 diff --git a/tests/Baseline/spicy.types.unit.basic-incremental/output b/tests/Baseline/spicy.types.unit.basic-incremental/output new file mode 100644 index 000000000..c3b535eb3 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.basic-incremental/output @@ -0,0 +1,4 @@ +[$f1=(not set), $f2=(not set)] +10000 +42 +[$f1=10000, $f2=42] diff --git a/tests/Baseline/spicy.types.unit.basic/.stderr b/tests/Baseline/spicy.types.unit.basic/.stderr new file mode 100644 index 000000000..c57ee8c7d --- /dev/null +++ b/tests/Baseline/spicy.types.unit.basic/.stderr @@ -0,0 +1,3 @@ +[spicy] Mini::Test +[spicy] f1 = 10000 +[spicy] f2 = 42 diff --git a/tests/Baseline/spicy.types.unit.basic/output b/tests/Baseline/spicy.types.unit.basic/output new file mode 100644 index 000000000..c3b535eb3 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.basic/output @@ -0,0 +1,4 @@ +[$f1=(not set), $f2=(not set)] +10000 +42 +[$f1=10000, $f2=42] diff --git a/tests/Baseline/spicy.types.unit.conditional-bytes/output b/tests/Baseline/spicy.types.unit.conditional-bytes/output new file mode 100644 index 000000000..d7a7e2856 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.conditional-bytes/output @@ -0,0 +1,2 @@ +[$len=2, $cond=0, $a=2, $x=(not set), $y=(not set), $z=3] +[$len=2, $cond=1, $a=2, $x=b"\x03", $y=4, $z=5] diff --git a/tests/Baseline/spicy.types.unit.conditional/output b/tests/Baseline/spicy.types.unit.conditional/output new file mode 100644 index 000000000..383f1ee68 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.conditional/output @@ -0,0 +1,4 @@ +[$f1=(not set), $f2=(not set)] +[$f1=10000, $f2=42] +[$f1=(not set), $f2=(not set)] +[$f1=0, $f2=(not set)] diff --git a/tests/Baseline/spicy.types.unit.convert-enum/output b/tests/Baseline/spicy.types.unit.convert-enum/output new file mode 100644 index 000000000..91cbe706f --- /dev/null +++ b/tests/Baseline/spicy.types.unit.convert-enum/output @@ -0,0 +1,3 @@ +ExampleEnum::A +ExampleEnum::B +ExampleEnum::C diff --git a/tests/Baseline/spicy.types.unit.convert-integer-signed/output b/tests/Baseline/spicy.types.unit.convert-integer-signed/output new file mode 100644 index 000000000..2c4d88c5d --- /dev/null +++ b/tests/Baseline/spicy.types.unit.convert-integer-signed/output @@ -0,0 +1 @@ +[$s1=-60, $s2=196] diff --git a/tests/Baseline/spicy.types.unit.convert-integer/output b/tests/Baseline/spicy.types.unit.convert-integer/output new file mode 100644 index 000000000..577ca5001 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.convert-integer/output @@ -0,0 +1 @@ +[$sl=30840, $sb=4328719365, $ul=21542142465, $ub=4328719365, $a=299700, $b=299700] diff --git a/tests/Baseline/spicy.types.unit.convert-regexp/output b/tests/Baseline/spicy.types.unit.convert-regexp/output new file mode 100644 index 000000000..dd5285912 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.convert-regexp/output @@ -0,0 +1 @@ +12346 diff --git a/tests/Baseline/spicy.types.unit.convert/output b/tests/Baseline/spicy.types.unit.convert/output new file mode 100644 index 000000000..d86a66ffa --- /dev/null +++ b/tests/Baseline/spicy.types.unit.convert/output @@ -0,0 +1,2 @@ +aabb +65535 diff --git a/tests/Baseline/spicy.types.unit.count-fail-2/output b/tests/Baseline/spicy.types.unit.count-fail-2/output new file mode 100644 index 000000000..f4fbba843 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.count-fail-2/output @@ -0,0 +1,2 @@ +[error] cannot coerce expression 'True' of type 'bool' to type 'uint<64>' +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.count-fail-3/output b/tests/Baseline/spicy.types.unit.count-fail-3/output new file mode 100644 index 000000000..67301e27f --- /dev/null +++ b/tests/Baseline/spicy.types.unit.count-fail-3/output @@ -0,0 +1,2 @@ +[error] cannot coerce expression '0x1p-1' of type 'real' to type 'uint<64>' +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.count-fail/output b/tests/Baseline/spicy.types.unit.count-fail/output new file mode 100644 index 000000000..9750ab08d --- /dev/null +++ b/tests/Baseline/spicy.types.unit.count-fail/output @@ -0,0 +1,2 @@ +[error] cannot coerce expression '-1' of type 'int<64>' to type 'uint<64>' +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.count/output b/tests/Baseline/spicy.types.unit.count/output new file mode 100644 index 000000000..93db0c4e0 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.count/output @@ -0,0 +1 @@ +[$a=[1], $b=[2]] diff --git a/tests/Baseline/spicy.types.unit.debug-hook/output b/tests/Baseline/spicy.types.unit.debug-hook/output new file mode 100644 index 000000000..d562b8bb8 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.debug-hook/output @@ -0,0 +1 @@ +DONE with %debug diff --git a/tests/Baseline/spicy.types.unit.dollardollar/output b/tests/Baseline/spicy.types.unit.dollardollar/output new file mode 100644 index 000000000..d84853d5a --- /dev/null +++ b/tests/Baseline/spicy.types.unit.dollardollar/output @@ -0,0 +1,11 @@ +3, 12 +4, 34 +1, 5 +2, 6 +[$y=b"6"] +5, [$y=b"6"] +6, 78 +|, 57 +|, 48 +7, ab +[$b=b"34", $c=b"78", $d=b"ab"] diff --git a/tests/Baseline/spicy.types.unit.error-hook-nested/output b/tests/Baseline/spicy.types.unit.error-hook-nested/output new file mode 100644 index 000000000..8a507eb72 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.error-hook-nested/output @@ -0,0 +1,3 @@ +[$a=b"1234", $t=(not set), $b=(not set)] +Error test2, [$x=b"567\n", $y=(not set)] +Error test1, [$a=b"1234", $t=[$x=b"567\n", $y=(not set)], $b=(not set)] diff --git a/tests/Baseline/spicy.types.unit.error-hook/output b/tests/Baseline/spicy.types.unit.error-hook/output new file mode 100644 index 000000000..e35034611 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.error-hook/output @@ -0,0 +1,2 @@ +[$a=b"1234", $b=(not set)] +Error, [$a=b"1234", $b=(not set)] diff --git a/tests/Baseline/spicy.types.unit.external-global-vars/output b/tests/Baseline/spicy.types.unit.external-global-vars/output new file mode 100644 index 000000000..13a876d04 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.external-global-vars/output @@ -0,0 +1 @@ +[$a=b"0", $b=[$a=b"1234", $b=b"567890"]] diff --git a/tests/Baseline/spicy.types.unit.external-hook/output b/tests/Baseline/spicy.types.unit.external-hook/output new file mode 100644 index 000000000..d6045f4bd --- /dev/null +++ b/tests/Baseline/spicy.types.unit.external-hook/output @@ -0,0 +1,5 @@ +567890 +A +A-module-global, [$a=b"1234", $b=(not set), $c=(not set)] +A-unit-global, [$a=b"1234", $b=(not set), $c=(not set)] +[$a=b"1234", $b=b"567890", $c=b"abcdef"] diff --git a/tests/Baseline/spicy.types.unit.external-hooks-import/output b/tests/Baseline/spicy.types.unit.external-hooks-import/output new file mode 100644 index 000000000..b5430d1a7 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.external-hooks-import/output @@ -0,0 +1,3 @@ +FOO init +FOO a, 1234 +FOO b, 567890 diff --git a/tests/Baseline/spicy.types.unit.external-hooks-same-module/output b/tests/Baseline/spicy.types.unit.external-hooks-same-module/output new file mode 100644 index 000000000..b5430d1a7 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.external-hooks-same-module/output @@ -0,0 +1,3 @@ +FOO init +FOO a, 1234 +FOO b, 567890 diff --git a/tests/Baseline/spicy.types.unit.external-hooks-unit-arg/output b/tests/Baseline/spicy.types.unit.external-hooks-unit-arg/output new file mode 100644 index 000000000..d97296115 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.external-hooks-unit-arg/output @@ -0,0 +1 @@ +2, [$a=b"12", $f=[$a=b"345", $b=b"67890"]] diff --git a/tests/Baseline/spicy.types.unit.field-default-value-fail/output b/tests/Baseline/spicy.types.unit.field-default-value-fail/output new file mode 100644 index 000000000..dd9df8bda --- /dev/null +++ b/tests/Baseline/spicy.types.unit.field-default-value-fail/output @@ -0,0 +1,2 @@ +[error] /Users/bbannier/src/spicy/tests/.tmp/spicy.types.unit.field-default-value-fail/field-default-value-fail.spicy:7: %default requires an argument +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.filter-chained/output b/tests/Baseline/spicy.types.unit.filter-chained/output new file mode 100644 index 000000000..6bbcf789a --- /dev/null +++ b/tests/Baseline/spicy.types.unit.filter-chained/output @@ -0,0 +1,2 @@ +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!"] +[$b1=b"Hello", $b2=b", Spi", $b3=b"cy!"] diff --git a/tests/Baseline/spicy.types.unit.filter-in-sub/output b/tests/Baseline/spicy.types.unit.filter-in-sub/output new file mode 100644 index 000000000..44fda9f7d --- /dev/null +++ b/tests/Baseline/spicy.types.unit.filter-in-sub/output @@ -0,0 +1,4 @@ +[$s1=b"FGH", $s2=b"IJ"] +[$a=b"abcde", $b=[$s1=b"FGH", $s2=b"IJ"]] +[$s1=b"FGH", $s2=b"IJ"] +[$a=b"abcde", $b=[$s1=b"FGH", $s2=b"IJ"]] diff --git a/tests/Baseline/spicy.types.unit.filter/output b/tests/Baseline/spicy.types.unit.filter/output new file mode 100644 index 000000000..74966e4ab --- /dev/null +++ b/tests/Baseline/spicy.types.unit.filter/output @@ -0,0 +1,2 @@ +[$a=b"AB", $b=b"CDEFG", $c=b"HIJ", $d=b"KLMNO"] +[$a=b"AB", $b=b"CDEFG", $c=b"HIJ", $d=b"KLMNO"] diff --git a/tests/Baseline/spicy.types.unit.hooks-across-imports/output b/tests/Baseline/spicy.types.unit.hooks-across-imports/output new file mode 100644 index 000000000..11050babb --- /dev/null +++ b/tests/Baseline/spicy.types.unit.hooks-across-imports/output @@ -0,0 +1,8 @@ +tA +tA: a, 12345 +tA: x-1, 66666 +tA: x-2, 66666 +tB +tB: x-1 $, 66666 +tB: x-2 $, 66666 +tB: y $, 77777 diff --git a/tests/Baseline/spicy.types.unit.input-save-and-restore/output b/tests/Baseline/spicy.types.unit.input-save-and-restore/output new file mode 100644 index 000000000..8d7eefcd5 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.input-save-and-restore/output @@ -0,0 +1 @@ +12345, 67890, 67890, 67890 diff --git a/tests/Baseline/spicy.types.unit.input/output b/tests/Baseline/spicy.types.unit.input/output new file mode 100644 index 000000000..2d9782277 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.input/output @@ -0,0 +1,14 @@ +Main:init, 0 +Sub:init, 0 +Sub:x, 0 +Sub:y, 0 +Main:Sub, 0 +Sub:init, 3 +Sub:x, 3 +Sub:y, 3 +Main:Sub, 0 +Sub:init, 6 +Sub:x, 6 +Sub:y, 6 +Main:Sub, 0 +Main:done, 0 diff --git a/tests/Baseline/spicy.types.unit.meta-data/output b/tests/Baseline/spicy.types.unit.meta-data/output new file mode 100644 index 000000000..dc24c75b5 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.meta-data/output @@ -0,0 +1,5 @@ +Available parsers: + + Mini::Test1 Hello, Parser! [80/tcp] + Mini::Test2 [123/udp, 567/tcp] [foo/bar, foo/baz] + diff --git a/tests/Baseline/spicy.types.unit.offset-early-access-fail/output b/tests/Baseline/spicy.types.unit.offset-early-access-fail/output new file mode 100644 index 000000000..3aec40415 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.offset-early-access-fail/output @@ -0,0 +1 @@ +[fatal error] terminating with uncaught exception of type hilti::rt::UnsetOptional: unset optional value (/home/robin/work/spicy/tests/.tmp/spicy.types.unit.offset-early-access-fail/offset-early-access-fail.spicy:16) diff --git a/tests/Baseline/spicy.types.unit.offset-indirect/output b/tests/Baseline/spicy.types.unit.offset-indirect/output new file mode 100644 index 000000000..57281dfa2 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.offset-indirect/output @@ -0,0 +1 @@ +[$x1=[$x=258, $y=3], $x2=[$x=1029, $y=6]] diff --git a/tests/Baseline/spicy.types.unit.offset-no-random-access-fail/output b/tests/Baseline/spicy.types.unit.offset-no-random-access-fail/output new file mode 100644 index 000000000..54375a756 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.offset-no-random-access-fail/output @@ -0,0 +1,2 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.unit.offset-no-random-access-fail/offset-no-random-access-fail.spicy:9: use of 'offset()' requires unit type to have property `%random-access` +[error] spicy-driver: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.offset/.stderr b/tests/Baseline/spicy.types.unit.offset/.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.unit.offset/output b/tests/Baseline/spicy.types.unit.offset/output new file mode 100644 index 000000000..1d6c4e15a --- /dev/null +++ b/tests/Baseline/spicy.types.unit.offset/output @@ -0,0 +1,14 @@ +Main:init, 0 +Sub:init, 0 +Sub:x, 0 +Sub:y, 2 +Main:Sub, 0 +Sub:init, 0 +Sub:x, 0 +Sub:y, 2 +Main:Sub, 3 +Sub:init, 0 +Sub:x, 0 +Sub:y, 2 +Main:Sub, 6 +Main:done, 6 diff --git a/tests/Baseline/spicy.types.unit.params-atomic/.stderr b/tests/Baseline/spicy.types.unit.params-atomic/.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.unit.params-atomic/output b/tests/Baseline/spicy.types.unit.params-atomic/output new file mode 100644 index 000000000..5f8bd5499 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.params-atomic/output @@ -0,0 +1 @@ +[$f=[$x="String", $y=True]] diff --git a/tests/Baseline/spicy.types.unit.params-forward/output b/tests/Baseline/spicy.types.unit.params-forward/output new file mode 100644 index 000000000..13a9214cd --- /dev/null +++ b/tests/Baseline/spicy.types.unit.params-forward/output @@ -0,0 +1,2 @@ +[$t1=b"yyy"] +[$x=[$t2=b"xxx", $x=]] diff --git a/tests/Baseline/spicy.types.unit.params-modify/.stderr b/tests/Baseline/spicy.types.unit.params-modify/.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.unit.params-modify/output b/tests/Baseline/spicy.types.unit.params-modify/output new file mode 100644 index 000000000..c14b0b681 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.params-modify/output @@ -0,0 +1,4 @@ +[$f=(not set), $x="original"] +[$x=b"xxx"] +[$f=[$x=b"xxx"], $x="original"] +[$f=[$x=b"xxx"], $x="modified"] diff --git a/tests/Baseline/spicy.types.unit.params-write-fail/output b/tests/Baseline/spicy.types.unit.params-write-fail/output new file mode 100644 index 000000000..7a07228a0 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.params-write-fail/output @@ -0,0 +1,3 @@ +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.unit.params-write-fail/params-write-fail.spicy:16: cannot assign to expression: x = False +[error] /home/robin/work/spicy/tests/.tmp/spicy.types.unit.params-write-fail/params-write-fail.spicy:17: cannot assign to expression: (*y).a = 42 +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.params/.stderr b/tests/Baseline/spicy.types.unit.params/.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.unit.params/output b/tests/Baseline/spicy.types.unit.params/output new file mode 100644 index 000000000..328830aeb --- /dev/null +++ b/tests/Baseline/spicy.types.unit.params/output @@ -0,0 +1,2 @@ +[$f=(not set)] +[$f=[$x=b"xxx"]] diff --git a/tests/Baseline/spicy.types.unit.set_input/output b/tests/Baseline/spicy.types.unit.set_input/output new file mode 100644 index 000000000..9715638db --- /dev/null +++ b/tests/Baseline/spicy.types.unit.set_input/output @@ -0,0 +1 @@ +[$x=[[$x=1, $y=2], [$x=772, $y=5], [$x=772, $y=5]]] diff --git a/tests/Baseline/spicy.types.unit.sub-unit/.stderr b/tests/Baseline/spicy.types.unit.sub-unit/.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Baseline/spicy.types.unit.sub-unit/output b/tests/Baseline/spicy.types.unit.sub-unit/output new file mode 100644 index 000000000..7539769d9 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.sub-unit/output @@ -0,0 +1,2 @@ +[$f=(not set)] +[$f=[$x=258, $y=3]] diff --git a/tests/Baseline/spicy.types.unit.switch-blocks-with-defaults/output b/tests/Baseline/spicy.types.unit.switch-blocks-with-defaults/output new file mode 100644 index 000000000..dee13c4cd --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-blocks-with-defaults/output @@ -0,0 +1,11 @@ +[$a=b"1", $b=b"2", $c=b"345", $d=(not set), $e=(not set), $f=(not set)] +2 +345 +noe_d +noe_e +noe_f +2 +345 +nope_d +nope_e +nope_f diff --git a/tests/Baseline/spicy.types.unit.switch-blocks-with-lists/output b/tests/Baseline/spicy.types.unit.switch-blocks-with-lists/output new file mode 100644 index 000000000..3685b241f --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-blocks-with-lists/output @@ -0,0 +1,3 @@ +[$a=b"A", $len=2, $dnets=[3, 4], $x=(not set), $b=b"\x05"] +x, [$a=b"B", $len=2, $dnets=(not set), $x=3, $b=(not set)] +[$a=b"B", $len=2, $dnets=[4], $x=3, $b=b"\x05"] diff --git a/tests/Baseline/spicy.types.unit.switch-blocks/output b/tests/Baseline/spicy.types.unit.switch-blocks/output new file mode 100644 index 000000000..6625dfcc9 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-blocks/output @@ -0,0 +1,3 @@ +[$a=b"1", $b=b"2", $c=b"345", $d=(not set)] +2 +345 diff --git a/tests/Baseline/spicy.types.unit.switch-bytes/output b/tests/Baseline/spicy.types.unit.switch-bytes/output new file mode 100644 index 000000000..1191247b6 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-bytes/output @@ -0,0 +1,2 @@ +1 +2 diff --git a/tests/Baseline/spicy.types.unit.switch-duplicate-case/output b/tests/Baseline/spicy.types.unit.switch-duplicate-case/output new file mode 100644 index 000000000..8f51ac605 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-duplicate-case/output @@ -0,0 +1,2 @@ +[error] /Users/robin/work/spicy/tests/.tmp/spicy.types.unit.switch-duplicate-case/switch-duplicate-case.spicy:14: duplicate case +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.switch-if/output b/tests/Baseline/spicy.types.unit.switch-if/output new file mode 100644 index 000000000..f1757fd48 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-if/output @@ -0,0 +1,2 @@ +[$a=b"1", $b1=b"2345", $b2=(not set)] +[$a=b"2", $b1=(not set), $b2=b"2345"] diff --git a/tests/Baseline/spicy.types.unit.switch-lahead-literal/output b/tests/Baseline/spicy.types.unit.switch-lahead-literal/output new file mode 100644 index 000000000..c8cd6c3f3 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-lahead-literal/output @@ -0,0 +1,2 @@ +[$a=(not set), $b=[$b=65535]] +[$a=[$a=b"A"], $b=(not set)] diff --git a/tests/Baseline/spicy.types.unit.switch-lahead-regexp-incremental/output b/tests/Baseline/spicy.types.unit.switch-lahead-regexp-incremental/output new file mode 100644 index 000000000..898fa96f5 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-lahead-regexp-incremental/output @@ -0,0 +1,4 @@ +A, xaaa, Foo +B, xaaaX, Bar +A, xaaa, Foo +B, xaaaX, Bar diff --git a/tests/Baseline/spicy.types.unit.switch-lahead/output b/tests/Baseline/spicy.types.unit.switch-lahead/output new file mode 100644 index 000000000..d62b3a28b --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-lahead/output @@ -0,0 +1,8 @@ +1, [$x=b"234"] +[$a=[$x=b"234"], $b=(not set), $c=(not set), $d=(not set), $y=b"567890"] +2, [$x=b"234"] +[$a=(not set), $b=[$x=b"234"], $c=(not set), $d=(not set), $y=b"567890"] +3, [$x=b"234"] +[$a=(not set), $b=(not set), $c=[$x=b"234"], $d=(not set), $y=b"567890"] +D, [$x=b"234"] +[$a=(not set), $b=(not set), $c=(not set), $d=[$x=b"234"], $y=b"567890"] diff --git a/tests/Baseline/spicy.types.unit.switch-no-default/output b/tests/Baseline/spicy.types.unit.switch-no-default/output new file mode 100644 index 000000000..a599b41a3 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-no-default/output @@ -0,0 +1,5 @@ +b1, 2345 +b2, +b1, +b2, 2345 +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: no matching case in switch statement (/home/robin/work/spicy/tests/.tmp/spicy.types.unit.switch-no-default/switch-no-default.spicy:13-16) diff --git a/tests/Baseline/spicy.types.unit.switch-scope/output b/tests/Baseline/spicy.types.unit.switch-scope/output new file mode 100644 index 000000000..29147516d --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-scope/output @@ -0,0 +1,2 @@ +1 +b, 2 diff --git a/tests/Baseline/spicy.types.unit.switch-shared-id/output b/tests/Baseline/spicy.types.unit.switch-shared-id/output new file mode 100644 index 000000000..5d2af8aee --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-shared-id/output @@ -0,0 +1,20 @@ +Hook! +Hook! +Hook! +Hook! +Hook2! +Hook2! +Hook2! +Hook2! +[$a=b"1", $b=b"2", $c=b"34567"] +[$a=b"2", $b=b"23", $c=b"45678"] +[$a=b"3", $b=b"234", $c=b"56789"] +[$a=b"4", $b=b"2345", $c=b"67890"] +b, 2 +b, 23 +b, 234 +b, 2345 +b1 +b1 +b1 +b1 diff --git a/tests/Baseline/spicy.types.unit.switch-void/output b/tests/Baseline/spicy.types.unit.switch-void/output new file mode 100644 index 000000000..b0497ba68 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-void/output @@ -0,0 +1,4 @@ +1 +2 or 3 +2 or 3 +something else diff --git a/tests/Baseline/spicy.types.unit.switch-wrong-case/output b/tests/Baseline/spicy.types.unit.switch-wrong-case/output new file mode 100644 index 000000000..ac785ec31 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-wrong-case/output @@ -0,0 +1,2 @@ +[error] /Users/robin/work/spicy/tests/.tmp/spicy.types.unit.switch-wrong-case/switch-wrong-case.spicy:13: cannot resolve operator: == > +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.switch/output b/tests/Baseline/spicy.types.unit.switch/output new file mode 100644 index 000000000..912da99dd --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch/output @@ -0,0 +1,28 @@ +[$a=b"1", $b1=b"2345", $b2=(not set), $b3=(not set), $c=b"67890"] +2345 +noe +noe +2345 +nope +nope +[$a=b"2", $b1=(not set), $b2=b"2345", $b3=(not set), $c=b"67890"] +noe +2345 +noe +nope +2345 +nope +[$a=b"3", $b1=(not set), $b2=b"2345", $b3=(not set), $c=b"67890"] +noe +2345 +noe +nope +2345 +nope +[$a=b"4", $b1=(not set), $b2=(not set), $b3=b"2345", $c=b"67890"] +noe +noe +2345 +nope +nope +2345 diff --git a/tests/Baseline/spicy.types.unit.transient/output b/tests/Baseline/spicy.types.unit.transient/output new file mode 100644 index 000000000..af2a35771 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.transient/output @@ -0,0 +1,4 @@ +[$y=b"6"] +|, 57 +|, 48 +[$b=b"34", $c=b"78", $d=b"ab"] diff --git a/tests/Baseline/spicy.types.unit.trimming/.stderr b/tests/Baseline/spicy.types.unit.trimming/.stderr new file mode 100644 index 000000000..198614a3f --- /dev/null +++ b/tests/Baseline/spicy.types.unit.trimming/.stderr @@ -0,0 +1,218 @@ +[spicy-verbose] - state: type=Mini::Test input="1234567890..." stream=0x6040000eabcd offsets=0/0/14 chunks=2 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Test -> f1 f2 f3 Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="1234567890..." stream=0x6040000eabcd offsets=0/0/14 chunks=2 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f1 -> b"12" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f1' to '12' +[spicy-verbose] - state: type=Mini::Test input="34567890ab..." stream=0x6040000eabcd offsets=2/2/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f2 -> b"34" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f2' to '34' +[spicy-verbose] - state: type=Mini::Test input="567890abcd" stream=0x6040000eabcd offsets=4/4/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f3 -> b"5678" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f3' to '5678' +[spicy-verbose] - state: type=Mini::Test input="90abcd" stream=0x6040000eabcd offsets=8/8/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Resolved: Mini_Sub -> Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="90abcd" stream=0x6040000eabcd offsets=8/8/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Sub -> x y +[spicy-verbose] - state: type=Mini::Sub input="90abcd" stream=0x6040000eabcd offsets=8/8/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: x -> b"90a" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'x' to '90a' +[spicy-verbose] - state: type=Mini::Sub input="bcd" stream=0x6040000eabcd offsets=11/11/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: y -> b"bcd" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'y' to 'bcd' +[spicy-verbose] - setting field 'f4' to '[$x=b"90a", $y=b"bcd"]' +[spicy-verbose] - state: type=Mini::Test input="1" stream=0x6040006f63e8 offsets=0/0/1 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Test -> f1 f2 f3 Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="1" stream=0x6040006f63e8 offsets=0/0/1 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f1 -> b"12" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x6040006f63e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f1' to '12' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x6040006f63e8 offsets=2/2/2 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f2 -> b"34" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 1 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x6040006f63e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f2' to '34' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x6040006f63e8 offsets=4/4/4 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f3 -> b"5678" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 1 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 3 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 3 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x6040006f63e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f3' to '5678' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x6040006f63e8 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Resolved: Mini_Sub -> Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="" stream=0x6040006f63e8 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Sub -> x y +[spicy-verbose] - state: type=Mini::Sub input="" stream=0x6040006f63e8 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: x -> b"90a" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 1 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 3 for stream 0x6040006f63e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'x' to '90a' +[spicy-verbose] - state: type=Mini::Sub input="" stream=0x6040006f63e8 offsets=11/11/11 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: y -> b"bcd" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 1 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x6040006f63e8 +[spicy-verbose] suspending to wait for more input for stream 0x6040006f63e8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 3 for stream 0x6040006f63e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'y' to 'bcd' +[spicy-verbose] - setting field 'f4' to '[$x=b"90a", $y=b"bcd"]' +[spicy-verbose] - state: type=Mini::Test input="12" stream=0x60400046aca8 offsets=0/0/2 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Test -> f1 f2 f3 Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="12" stream=0x60400046aca8 offsets=0/0/2 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f1 -> b"12" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f1' to '12' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400046aca8 offsets=2/2/2 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f2 -> b"34" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400046aca8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x60400046aca8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f2' to '34' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400046aca8 offsets=4/4/4 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f3 -> b"5678" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400046aca8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x60400046aca8 +[spicy-verbose] suspending to wait for more input for stream 0x60400046aca8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x60400046aca8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f3' to '5678' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400046aca8 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Resolved: Mini_Sub -> Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400046aca8 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Sub -> x y +[spicy-verbose] - state: type=Mini::Sub input="" stream=0x60400046aca8 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: x -> b"90a" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400046aca8, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 2 for stream 0x60400046aca8 +[spicy-verbose] suspending to wait for more input for stream 0x60400046aca8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x60400046aca8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'x' to '90a' +[spicy-verbose] - state: type=Mini::Sub input="b" stream=0x60400046aca8 offsets=11/11/12 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: y -> b"bcd" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400046aca8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 3 for stream 0x60400046aca8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'y' to 'bcd' +[spicy-verbose] - setting field 'f4' to '[$x=b"90a", $y=b"bcd"]' +[spicy-verbose] - state: type=Mini::Test input="123" stream=0x6040006695a8 offsets=0/0/3 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Test -> f1 f2 f3 Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="123" stream=0x6040006695a8 offsets=0/0/3 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f1 -> b"12" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f1' to '12' +[spicy-verbose] - state: type=Mini::Test input="3" stream=0x6040006695a8 offsets=2/2/3 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f2 -> b"34" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006695a8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x6040006695a8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f2' to '34' +[spicy-verbose] - state: type=Mini::Test input="56" stream=0x6040006695a8 offsets=4/4/6 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f3 -> b"5678" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006695a8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 5 for stream 0x6040006695a8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f3' to '5678' +[spicy-verbose] - state: type=Mini::Test input="9" stream=0x6040006695a8 offsets=8/8/9 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Resolved: Mini_Sub -> Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="9" stream=0x6040006695a8 offsets=8/8/9 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Sub -> x y +[spicy-verbose] - state: type=Mini::Sub input="9" stream=0x6040006695a8 offsets=8/8/9 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: x -> b"90a" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006695a8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x6040006695a8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'x' to '90a' +[spicy-verbose] - state: type=Mini::Sub input="b" stream=0x6040006695a8 offsets=11/11/12 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: y -> b"bcd" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040006695a8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 3 for stream 0x6040006695a8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'y' to 'bcd' +[spicy-verbose] - setting field 'f4' to '[$x=b"90a", $y=b"bcd"]' +[spicy-verbose] - state: type=Mini::Test input="1234" stream=0x60400099d768 offsets=0/0/4 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Test -> f1 f2 f3 Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="1234" stream=0x60400099d768 offsets=0/0/4 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f1 -> b"12" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f1' to '12' +[spicy-verbose] - state: type=Mini::Test input="34" stream=0x60400099d768 offsets=2/2/4 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f2 -> b"34" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f2' to '34' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400099d768 offsets=4/4/4 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f3 -> b"5678" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400099d768, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x60400099d768 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f3' to '5678' +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400099d768 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Resolved: Mini_Sub -> Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="" stream=0x60400099d768 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Sub -> x y +[spicy-verbose] - state: type=Mini::Sub input="" stream=0x60400099d768 offsets=8/8/8 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: x -> b"90a" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400099d768, currently have 0 +[spicy-verbose] resuming after insufficient input, now have 4 for stream 0x60400099d768 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'x' to '90a' +[spicy-verbose] - state: type=Mini::Sub input="b" stream=0x60400099d768 offsets=11/11/12 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: y -> b"bcd" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x60400099d768, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 3 for stream 0x60400099d768 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'y' to 'bcd' +[spicy-verbose] - setting field 'f4' to '[$x=b"90a", $y=b"bcd"]' +[spicy-verbose] - state: type=Mini::Test input="12345" stream=0x6040001412e8 offsets=0/0/5 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Test -> f1 f2 f3 Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="12345" stream=0x6040001412e8 offsets=0/0/5 chunks=2 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f1 -> b"12" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f1' to '12' +[spicy-verbose] - state: type=Mini::Test input="345" stream=0x6040001412e8 offsets=2/2/5 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f2 -> b"34" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f2' to '34' +[spicy-verbose] - state: type=Mini::Test input="5" stream=0x6040001412e8 offsets=4/4/5 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: f3 -> b"5678" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040001412e8, currently have 1 +[spicy-verbose] resuming after insufficient input, now have 6 for stream 0x6040001412e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'f3' to '5678' +[spicy-verbose] - state: type=Mini::Test input="90" stream=0x6040001412e8 offsets=8/8/10 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Resolved: Mini_Sub -> Mini_Sub +[spicy-verbose] - state: type=Mini::Test input="90" stream=0x6040001412e8 offsets=8/8/10 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Unit: Mini_Sub -> x y +[spicy-verbose] - state: type=Mini::Sub input="90" stream=0x6040001412e8 offsets=8/8/10 chunks=1 frozen=no mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: x -> b"90a" (bytes) +[spicy-verbose] suspending to wait for more input for stream 0x6040001412e8, currently have 2 +[spicy-verbose] resuming after insufficient input, now have 6 for stream 0x6040001412e8 +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'x' to '90a' +[spicy-verbose] - state: type=Mini::Sub input="bcd" stream=0x6040001412e8 offsets=11/11/14 chunks=1 frozen=yes mode=default trim=yes lah=n/a lah_token="n/a" +[spicy-verbose] - parsing production: Ctor: y -> b"bcd" (bytes) +[spicy-verbose] - trimming input +[spicy-verbose] - setting field 'y' to 'bcd' +[spicy-verbose] - setting field 'f4' to '[$x=b"90a", $y=b"bcd"]' diff --git a/tests/Baseline/spicy.types.unit.trimming/output b/tests/Baseline/spicy.types.unit.trimming/output new file mode 100644 index 000000000..f635f693e --- /dev/null +++ b/tests/Baseline/spicy.types.unit.trimming/output @@ -0,0 +1,6 @@ +[$f1=b"12", $f2=b"34", $f3=b"5678", $f4=[$x=b"90a", $y=b"bcd"]] +[$f1=b"12", $f2=b"34", $f3=b"5678", $f4=[$x=b"90a", $y=b"bcd"]] +[$f1=b"12", $f2=b"34", $f3=b"5678", $f4=[$x=b"90a", $y=b"bcd"]] +[$f1=b"12", $f2=b"34", $f3=b"5678", $f4=[$x=b"90a", $y=b"bcd"]] +[$f1=b"12", $f2=b"34", $f3=b"5678", $f4=[$x=b"90a", $y=b"bcd"]] +[$f1=b"12", $f2=b"34", $f3=b"5678", $f4=[$x=b"90a", $y=b"bcd"]] diff --git a/tests/Baseline/spicy.types.unit.variables-optional/output b/tests/Baseline/spicy.types.unit.variables-optional/output new file mode 100644 index 000000000..dc070569e --- /dev/null +++ b/tests/Baseline/spicy.types.unit.variables-optional/output @@ -0,0 +1,2 @@ +[$f=(not set), $x=(not set)] +[$f=b"xxx", $x=b"xxx"] diff --git a/tests/Baseline/spicy.types.unit.variables/output b/tests/Baseline/spicy.types.unit.variables/output new file mode 100644 index 000000000..9ae4c623c --- /dev/null +++ b/tests/Baseline/spicy.types.unit.variables/output @@ -0,0 +1,3 @@ +[$f=(not set), $x=b"", $y=b"yyy"] +[$f=b"xxx", $x=b"xxx", $y=b"yyy"] +yyy diff --git a/tests/Baseline/spicy.types.vector.iterator/output b/tests/Baseline/spicy.types.vector.iterator/output new file mode 100644 index 000000000..3c15c7876 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.iterator/output @@ -0,0 +1,3 @@ +49 +50 +51 diff --git a/tests/Baseline/spicy.types.vector.parse-const/output b/tests/Baseline/spicy.types.vector.parse-const/output new file mode 100644 index 000000000..7e854b14b --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-const/output @@ -0,0 +1 @@ +[$x=[1, 1, 1]] diff --git a/tests/Baseline/spicy.types.vector.parse-ctor/output b/tests/Baseline/spicy.types.vector.parse-ctor/output new file mode 100644 index 000000000..76d6cc89d --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-ctor/output @@ -0,0 +1,6 @@ +[$lines1=(not set), $lines2=(not set)] +item1: XXX +item1: XXX +item2: XXX +item2: XXX +[$lines1=[b"XXX", b"XXX"], $lines2=[b"XXX", b"XXX"]] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-ctor/output b/tests/Baseline/spicy.types.vector.parse-lahead-ctor/output new file mode 100644 index 000000000..c9120a7b0 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-ctor/output @@ -0,0 +1,5 @@ +[$lines=(not set), $dashes=(not set), $last=(not set)] + item: [$line=b"XXX\\n"] + item: [$line=b"XXX\\n"] + item: [$line=b"XXX\\n"] +[$lines=[[$line=b"XXX\n"], [$line=b"XXX\n"], [$line=b"XXX\n"]], $dashes=b"---\n", $last=b"XXX\n"] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-int-ambigious/output b/tests/Baseline/spicy.types.vector.parse-lahead-int-ambigious/output new file mode 100644 index 000000000..bdc6b6e93 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-int-ambigious/output @@ -0,0 +1,3 @@ +[error] /Users/robin/work/spicy/tests/.tmp/spicy.types.vector.parse-lahead-int-ambigious/parse-lahead-int-ambigious.spicy:12-17: grammar HTTP::Test (/Users/robin/work/spicy/tests/.tmp/spicy.types.vector.parse-lahead-int-ambigious/parse-lahead-int-ambigious.spicy:12-17), production a_l1 (/Users/robin/work/spicy/tests/.tmp/spicy.types.vector.parse-lahead-int-ambigious/parse-lahead-int-ambigious.spicy:13) is ambigious for look-ahead symbol(s) { 1 (uint<8>) } + +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-int-longest-match/output b/tests/Baseline/spicy.types.vector.parse-lahead-int-longest-match/output new file mode 100644 index 000000000..d8c017a26 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-int-longest-match/output @@ -0,0 +1 @@ +[$a=[[$x=1, $y=2571], [$x=1, $y=3085]], $b=258] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-int-regexp-longest-match/output b/tests/Baseline/spicy.types.vector.parse-lahead-int-regexp-longest-match/output new file mode 100644 index 000000000..def277809 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-int-regexp-longest-match/output @@ -0,0 +1,2 @@ +[$a=[[$x=65, $y=2571], [$x=65, $y=3085]], $b=b"AAA", $c=5] +[$a=[], $b=b"AAA", $c=5] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-int-regexp/output b/tests/Baseline/spicy.types.vector.parse-lahead-int-regexp/output new file mode 100644 index 000000000..cacb5c900 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-int-regexp/output @@ -0,0 +1,2 @@ +[$a=[[$x=1, $y=2571], [$x=1, $y=3085]], $b=b"XXX", $c=5] +[$a=[], $b=b"XXX", $c=5] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-int-variable/output b/tests/Baseline/spicy.types.vector.parse-lahead-int-variable/output new file mode 100644 index 000000000..bb289fc51 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-int-variable/output @@ -0,0 +1 @@ +[$a=[[$x=1, $y=2571], [$x=1, $y=3085]], $b=2] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-int/output b/tests/Baseline/spicy.types.vector.parse-lahead-int/output new file mode 100644 index 000000000..d4476445d --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-int/output @@ -0,0 +1,3 @@ +[$a=[[$x=1, $y=2571], [$x=1, $y=3085]], $b=2, $c=3599] +[$a=[], $b=2, $c=3599] +[fatal error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: no expected look-ahead token found (/home/robin/work/spicy/tests/.tmp/spicy.types.vector.parse-lahead-int/parse-lahead-int.spicy:15) diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-mixed/output b/tests/Baseline/spicy.types.vector.parse-lahead-mixed/output new file mode 100644 index 000000000..d20c3445d --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-mixed/output @@ -0,0 +1,5 @@ +[$lines=(not set), $dashes=(not set), $last=(not set)] + item: [$line=b"AAA\\n"] + item: [$line=b"BBB\\n"] + item: [$line=b"CCC\\n"] +[$lines=[[$line=b"AAA\n"], [$line=b"BBB\n"], [$line=b"CCC\n"]], $dashes=b"---\n", $last=b"XXX\n"] diff --git a/tests/Baseline/spicy.types.vector.parse-lahead-regexp/output b/tests/Baseline/spicy.types.vector.parse-lahead-regexp/output new file mode 100644 index 000000000..d20c3445d --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-lahead-regexp/output @@ -0,0 +1,5 @@ +[$lines=(not set), $dashes=(not set), $last=(not set)] + item: [$line=b"AAA\\n"] + item: [$line=b"BBB\\n"] + item: [$line=b"CCC\\n"] +[$lines=[[$line=b"AAA\n"], [$line=b"BBB\n"], [$line=b"CCC\n"]], $dashes=b"---\n", $last=b"XXX\n"] diff --git a/tests/Baseline/spicy.types.vector.parse-length/output b/tests/Baseline/spicy.types.vector.parse-length/output new file mode 100644 index 000000000..70f3aa8c9 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-length/output @@ -0,0 +1 @@ +[$a=[[$x=1, $y=2], [$x=3, $y=4]], $b=b"XXXX"] diff --git a/tests/Baseline/spicy.types.vector.parse-regexp/output b/tests/Baseline/spicy.types.vector.parse-regexp/output new file mode 100644 index 000000000..59ecb8c1b --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-regexp/output @@ -0,0 +1,12 @@ +[$lines=(not set)] +line: ABC + +line: DEF + +line: GHI + +line: + +line: XYZ + +[$lines=[b"ABC\n", b"DEF\n", b"GHI\n", b"\n", b"XYZ\n"]] diff --git a/tests/Baseline/spicy.types.vector.parse-size-stop/output b/tests/Baseline/spicy.types.vector.parse-size-stop/output new file mode 100644 index 000000000..47ee3c5bc --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-size-stop/output @@ -0,0 +1,7 @@ +[$lines=(not set), $dashes=(not set), $last=(not set)] +[0] [] + -> new item is [$line=b"XXX\\n"] +[1] [[$line=b"XXX\\n"]] + -> new item is [$line=b"XXX\\n"] +[2] [[$line=b"XXX\\n"], [$line=b"XXX\\n"]] +[$lines=[[$line=b"XXX\n"], [$line=b"XXX\n"]], $dashes=b"---\n", $last=b"XXX\n"] diff --git a/tests/Baseline/spicy.types.vector.parse-size/output b/tests/Baseline/spicy.types.vector.parse-size/output new file mode 100644 index 000000000..e040a84c4 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-size/output @@ -0,0 +1,5 @@ +[$lines=(not set), $dashes=(not set), $last=(not set)] +item: [$line=b"XXX\\n"] +item: [$line=b"XXX\\n"] +item: [$line=b"XXX\\n"] +[$lines=[[$line=b"XXX\n"], [$line=b"XXX\n"], [$line=b"XXX\n"]], $dashes=b"---\n", $last=b"XXX\n"] diff --git a/tests/Baseline/spicy.types.vector.parse-subitem/output b/tests/Baseline/spicy.types.vector.parse-subitem/output new file mode 100644 index 000000000..820b4da21 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-subitem/output @@ -0,0 +1,4 @@ +97, 98 +97, 99 +97, 100 +[$x=97, $y=[[$z=98], [$z=99], [$z=100]]] diff --git a/tests/Baseline/spicy.types.vector.parse-type/output b/tests/Baseline/spicy.types.vector.parse-type/output new file mode 100644 index 000000000..f123499a9 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-type/output @@ -0,0 +1,5 @@ +[$x=(not set)] +one: 97 +one: 98 +one: 99 +[$x=[97, 98, 99]] diff --git a/tests/Baseline/spicy.types.vector.parse-until-including/output b/tests/Baseline/spicy.types.vector.parse-until-including/output new file mode 100644 index 000000000..8e9b0a202 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-until-including/output @@ -0,0 +1,4 @@ +1 +2 +3 +[$ints=[1, 2, 3], $last=4] diff --git a/tests/Baseline/spicy.types.vector.parse-until/output b/tests/Baseline/spicy.types.vector.parse-until/output new file mode 100644 index 000000000..956939641 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-until/output @@ -0,0 +1,6 @@ +[$line=b"AAA\n"] +[$line=b"BBB\n"] +[$line=b"CCC\n"] +[$line=b"DDD\n"] +[$line=b"EEEE\n"] +[$lines=[[$line=b"AAA\n"], [$line=b"BBB\n"], [$line=b"CCC\n"], [$line=b"DDD\n"], [$line=b"EEEE\n"]], $last=b"FFF\n"] diff --git a/tests/Baseline/spicy.types.vector.parse-while/output b/tests/Baseline/spicy.types.vector.parse-while/output new file mode 100644 index 000000000..2f4460690 --- /dev/null +++ b/tests/Baseline/spicy.types.vector.parse-while/output @@ -0,0 +1,3 @@ +1 +2 +[$ints=[1, 2], $last=4] diff --git a/tests/Baseline/zeek.evt.dns/output b/tests/Baseline/zeek.evt.dns/output new file mode 100644 index 000000000..d53b87e45 --- /dev/null +++ b/tests/Baseline/zeek.evt.dns/output @@ -0,0 +1,3 @@ +[orig_h=192.150.186.169, orig_p=55587/udp, resp_h=192.150.186.8, resp_p=53/udp], [id=29622, opcode=0, rcode=0, QR=F, AA=F, TC=F, RD=T, RA=F, Z=0, num_queries=1, num_answers=0, num_auth=0, num_addl=0], www.heise.de, 1, 1 +[orig_h=192.150.186.169, orig_p=55588/udp, resp_h=192.150.186.8, resp_p=53/udp], [id=15429, opcode=0, rcode=0, QR=F, AA=F, TC=F, RD=T, RA=F, Z=0, num_queries=1, num_answers=0, num_auth=0, num_addl=0], www.google.com, 1, 1 +[orig_h=192.150.186.169, orig_p=55589/udp, resp_h=192.150.186.8, resp_p=53/udp], [id=27360, opcode=0, rcode=0, QR=F, AA=F, TC=F, RD=T, RA=F, Z=0, num_queries=1, num_answers=0, num_auth=0, num_addl=0], www.net.in.tum.de, 1, 1 diff --git a/tests/Baseline/zeek.evt.double-event/output b/tests/Baseline/zeek.evt.double-event/output new file mode 100644 index 000000000..8c9e00749 --- /dev/null +++ b/tests/Baseline/zeek.evt.double-event/output @@ -0,0 +1,4 @@ +1, OpenSSH_3.8.1p1 +1, OpenSSH_3.9p1 +2, OpenSSH_3.8.1p1 +2, OpenSSH_3.9p1 diff --git a/tests/Baseline/zeek.evt.double-types/output b/tests/Baseline/zeek.evt.double-types/output new file mode 100644 index 000000000..2d23dab57 --- /dev/null +++ b/tests/Baseline/zeek.evt.double-types/output @@ -0,0 +1,3 @@ +dtest_message, dtest::FUNCS_YES +dtest_result, dtest::RESULT_YES_AGAIN +dtest_result_tuple, dtest::FUNCS_YES, dtest::RESULT_YES_AGAIN diff --git a/tests/Baseline/zeek.evt.event-cond/output b/tests/Baseline/zeek.evt.event-cond/output new file mode 100644 index 000000000..e6c7b6e58 --- /dev/null +++ b/tests/Baseline/zeek.evt.event-cond/output @@ -0,0 +1,5 @@ +1, OpenSSH_3.8.1p1 +1, OpenSSH_3.9p1 +3, OpenSSH_3.9p1 +4, OpenSSH_3.8.1p1 +5, OpenSSH_3.8.1p1 diff --git a/tests/Baseline/zeek.evt.export-enum/output b/tests/Baseline/zeek.evt.export-enum/output new file mode 100644 index 000000000..9ec148130 --- /dev/null +++ b/tests/Baseline/zeek.evt.export-enum/output @@ -0,0 +1 @@ +TupleEnum::TestEnum_A diff --git a/tests/Baseline/zeek.evt.file-analyzer/output b/tests/Baseline/zeek.evt.file-analyzer/output new file mode 100644 index 000000000..005bc0c4f --- /dev/null +++ b/tests/Baseline/zeek.evt.file-analyzer/output @@ -0,0 +1 @@ +text data, FutcX72nUj1Xe6rSG4, hello world diff --git a/tests/Baseline/zeek.evt.import-from/output b/tests/Baseline/zeek.evt.import-from/output new file mode 100644 index 000000000..8f838c77b --- /dev/null +++ b/tests/Baseline/zeek.evt.import-from/output @@ -0,0 +1,2 @@ +Foo::x, Foo::y +Foo::x, Foo::y diff --git a/tests/Baseline/zeek.evt.list-conversion/output b/tests/Baseline/zeek.evt.list-conversion/output new file mode 100644 index 000000000..c7aa11ff3 --- /dev/null +++ b/tests/Baseline/zeek.evt.list-conversion/output @@ -0,0 +1,5 @@ +[orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp] +T +[[a=83, b=84], [a=83, b=84], [a=72, b=73], [a=45, b=46], [a=50, b=51]] +11824 +11599 diff --git a/tests/Baseline/zeek.evt.multiple-enum/output b/tests/Baseline/zeek.evt.multiple-enum/output new file mode 100644 index 000000000..edeb1bc73 --- /dev/null +++ b/tests/Baseline/zeek.evt.multiple-enum/output @@ -0,0 +1 @@ +one, dtest::RESULT_B diff --git a/tests/Baseline/zeek.evt.replaces/output b/tests/Baseline/zeek.evt.replaces/output new file mode 100644 index 000000000..a4c8c2799 --- /dev/null +++ b/tests/Baseline/zeek.evt.replaces/output @@ -0,0 +1,3 @@ +Analyzer::ANALYZER_SSH, 3 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], F, 1.99, OpenSSH_3.9p1 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], T, 2.0, OpenSSH_3.8.1p1 diff --git a/tests/Baseline/zeek.evt.ssh-banner-precompiled/output b/tests/Baseline/zeek.evt.ssh-banner-precompiled/output new file mode 100644 index 000000000..738a63d57 --- /dev/null +++ b/tests/Baseline/zeek.evt.ssh-banner-precompiled/output @@ -0,0 +1,4 @@ +Analyzer::ANALYZER_SPICY_SSH, 4 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], F, 1.99, OpenSSH_3.9p1 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], T, 2.0, OpenSSH_3.8.1p1 +Analyzer::ANALYZER_SSH, 3 diff --git a/tests/Baseline/zeek.evt.ssh-banner/output b/tests/Baseline/zeek.evt.ssh-banner/output new file mode 100644 index 000000000..738a63d57 --- /dev/null +++ b/tests/Baseline/zeek.evt.ssh-banner/output @@ -0,0 +1,4 @@ +Analyzer::ANALYZER_SPICY_SSH, 4 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], F, 1.99, OpenSSH_3.9p1 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], T, 2.0, OpenSSH_3.8.1p1 +Analyzer::ANALYZER_SSH, 3 diff --git a/tests/Baseline/zeek.evt.tls.tls/google b/tests/Baseline/zeek.evt.tls.tls/google new file mode 100644 index 000000000..276922a28 --- /dev/null +++ b/tests/Baseline/zeek.evt.tls.tls/google @@ -0,0 +1,35 @@ +==Client hello +192.168.6.157, 74.125.239.63 +Version, TLS::TLSVersion_TLSv12 +ts, 1393273890 +client random, }\xbb\xddj\xe3\x87\x00*M\xed\xa3Lr\x01\xd2C\xf2/\xc3\x1f\xbb\xa1\xca\x15%5\xb0m +session id, +cipher list, [49195, 49199, 158, 52244, 52243, 156, 49162, 49172, 57, 53, 49159, 49161, 49169, 49171, 51, 50, 5, 4, 47, 10] +==Client hello end +tls_extension_server_name, 192.168.6.157, 74.125.239.63, [www.google.fr] +tls_extension_renegotiation_info, 192.168.6.157, 74.125.239.63, +tls_extension_elliptic_curves, 192.168.6.157, 74.125.239.63, [23, 24, 25] +tls_extension_ec_point_formats, 192.168.6.157, 74.125.239.63 +tls_extension_sessionticket_tls, 192.168.6.157, 74.125.239.63, +tls_extension_next_protocol_negotiation, 192.168.6.157, 74.125.239.63 +tls_extension_application_layer_protocol_negotiation, 192.168.6.157, 74.125.239.63, [spdy/3, spdy/3.1, http/1.1] +unknown extension, 192.168.6.157, 74.125.239.63, 30032, TLS::Extensions_channel_id_new, +tls_extension_status_request, 192.168.6.157, 74.125.239.63, , +tls_extension_signature_algorithms, 192.168.6.157, 74.125.239.63, [[hash=TLS::HashAlgorithm_sha256, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha384, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha1, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha256, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha384, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha1, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha256, signature=TLS::SignatureAlgorithm_dsa], [hash=TLS::HashAlgorithm_sha1, signature=TLS::SignatureAlgorithm_dsa]] +tls_extension_signed_certificate_timestamp, 192.168.6.157, 74.125.239.63, +unknown extension, 192.168.6.157, 74.125.239.63, 35655, TLS::Extensions_padding, \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 +==Server hello +192.168.6.157, 74.125.239.63 +Version, TLS::TLSVersion_TLSv12 +ts, 1393273889 +server random, R\xfaw\xed\xdd\x86\xa768$I\x95\xebr\xb0\xe5\xd8\xfeyb\xeaLKB!W:\x0a +session id, +cipher, 49195 +compression metod, 0 +==Server hello end +tls_extension_renegotiation_info, 192.168.6.157, 74.125.239.63, +tls_extension_ec_point_formats, 192.168.6.157, 74.125.239.63 +tls_extension_sessionticket_tls, 192.168.6.157, 74.125.239.63, +unknown extension, 192.168.6.157, 74.125.239.63, 30032, TLS::Extensions_channel_id_new, +tls_extension_application_layer_protocol_negotiation, 192.168.6.157, 74.125.239.63, [spdy/3.1] +session ticket handshake, 100800, \x97\x85\xf0_\xed\x87M\xa4_"\xe7L\xb6\xd5"\xbek$j{\x06u\xdc\x11\x9b\x0d\xff\xe8\x9e\xe5\xaf\xb21{N\xb4M\xcd\xe5v]\x03V\x8c\xbb\x0c\x0b^\xc3\xd8\xca\x00{\xed\x814\x9f\x03zP(X\x89ndk\xc9\x04\xeb|\x99\xd0\xaf\x8a\x95J\xfe\x97K\x80\x1e\xc1\xdc\x1b\x8b\x8c\xdd6\xeb\xb6\x7f\x98@\x82\x82xf\xdc\xdc\x11g\x93\x8dTI\x0f\xb9\x05\xc9\xa9\xdcqaR\x8d\x1c\xd8G\xd7\xbc\xc6\xf1A\xe7\xbbu\xca\x0f\x98\xc8T\xf5\xbfyE\xd8\xafu\xe1\xed\xbc\x0e\xc2\x82\x09n\xa6)\x84\xd9\x81!\xa7\x9c\x12\x9e\x03p'\x81\xd2\x1c\xc8X;\x0e\x96p$\xa0>xkuH>\x04\x10\x1e\x15\xa6\x04\xbcP\x82\xd3GK6\xc4\x1f\x1a\xf2w\x19\xed\xd07\x16\xb23\x0bJ\xea#\xc0:d1\x03-\xd0 diff --git a/tests/Baseline/zeek.evt.tls.tls/tls-extensions b/tests/Baseline/zeek.evt.tls.tls/tls-extensions new file mode 100644 index 000000000..91b1266df --- /dev/null +++ b/tests/Baseline/zeek.evt.tls.tls/tls-extensions @@ -0,0 +1,28 @@ +==Client hello +192.168.1.105, 74.125.224.79 +Version, TLS::TLSVersion_TLSv10 +ts, 1335538392 +client random, \xc97\x10\xcfx\x9e\xf7\xaa\x0e\xd1\xdd\x9b\x8c\xd5\xcb\xc5T\xa9\xda\xfe\xc9\x931\x99\xd5BO\xd7 +session id, +cipher list, [49162, 49172, 136, 135, 57, 56, 49167, 49157, 132, 53, 49159, 49161, 49169, 49171, 69, 68, 102, 51, 50, 49164, 49166, 49154, 49156, 150, 65, 4, 5, 47, 49160, 49170, 22, 19, 49165, 49155, 65279, 10] +==Client hello end +tls_extension_server_name, 192.168.1.105, 74.125.224.79, [ssl.gstatic.com] +tls_extension_renegotiation_info, 192.168.1.105, 74.125.224.79, +tls_extension_elliptic_curves, 192.168.1.105, 74.125.224.79, [23, 24, 25] +tls_extension_ec_point_formats, 192.168.1.105, 74.125.224.79 +tls_extension_sessionticket_tls, 192.168.1.105, 74.125.224.79, +tls_extension_next_protocol_negotiation, 192.168.1.105, 74.125.224.79 +==Server hello +192.168.1.105, 74.125.224.79 +Version, TLS::TLSVersion_TLSv10 +ts, 1335538392 +server random, I\x1bx\xcae\x04\xb9$+\xbc\xc2\xb7\xc9\x82_\xc0\xa0\x8cE=\xcb\xa9\xf0\xb3\xb0\xcbH\xdf +session id, +cipher, 49169 +compression metod, 0 +==Server hello end +tls_extension_renegotiation_info, 192.168.1.105, 74.125.224.79, +tls_extension_ec_point_formats, 192.168.1.105, 74.125.224.79 +tls_extension_sessionticket_tls, 192.168.1.105, 74.125.224.79, +tls_extension_next_protocol_negotiation, 192.168.1.105, 74.125.224.79 +session ticket handshake, 100800, x}A\xa0\xd3b&\xa2\xe5\xc4^\x9f\xc5\xd6rX\x95M\xfe\xae\xcaXX\xe0O\x16@[\x03^A\x8b7\xb5\xaeA=tk\xf5o\xe8y\xa1\x8ag\xe2\\xd4\xb3\xe5,>\xd8\x9a\xf0\xe0\x11\x82\xbf\xcb\x92\xb0\x85\xe2%d\xee\xd5\x08\x1cY^\xd8;\x0d\xfd\xd5E\xe7\x1b\xc1\x11@\xdf\x0e\x8d\xd9\xf9\x04L8-\xd8=\x03\xa6\x12\xac6\xb9\xeaq\x90{\xe7B\x0d\x1a\xb0\x87\x7f7`\x81\x0c\xe9Z"J,\xc1\x89\xf8\xbd\x95J`{\x986\x1eT\xf2\xfe$\x8a]\xb3\x96\xe6\xf9\x83\xad+M\xf0l\x7f\xa7\xe3\x1bNu\x14|`\xfb\x9cn\xec\xd4\x14\x8b diff --git a/tests/Baseline/zeek.evt.tls.tls/tls1.2 b/tests/Baseline/zeek.evt.tls.tls/tls1.2 new file mode 100644 index 000000000..56af39e71 --- /dev/null +++ b/tests/Baseline/zeek.evt.tls.tls/tls1.2 @@ -0,0 +1,23 @@ +==Client hello +10.0.0.80, 68.233.76.12 +Version, TLS::TLSVersion_TLSv12 +ts, 1357328848 +client random, 8\xd0U@\xf1\xaamI\xb5SE\x0b\x82\xa4\xe0\x9eG\xf3\xdd\x1f\xeey\xa6[\xcc\xd7\x04\x90 +session id, +cipher list, [49200, 49196, 49192, 49188, 49172, 49162, 49186, 49185, 163, 159, 107, 106, 57, 56, 136, 135, 49202, 49198, 49194, 49190, 49167, 49157, 157, 61, 53, 132, 49170, 49160, 49180, 49179, 22, 19, 49165, 49155, 10, 49199, 49195, 49191, 49187, 49171, 49161, 49183, 49182, 162, 158, 103, 64, 51, 50, 154, 153, 69, 68, 49201, 49197, 49193, 49189, 49166, 49156, 156, 60, 47, 150, 65, 7, 49169, 49159, 49164, 49154, 5, 4, 21, 18, 9, 20, 17, 8, 6, 3, 255] +==Client hello end +tls_extension_ec_point_formats, 10.0.0.80, 68.233.76.12 +tls_extension_elliptic_curves, 10.0.0.80, 68.233.76.12, [14, 13, 25, 11, 12, 24, 9, 10, 22, 23, 8, 6, 7, 20, 21, 4, 5, 18, 19, 1, 2, 3, 15, 16, 17] +tls_extension_sessionticket_tls, 10.0.0.80, 68.233.76.12, +tls_extension_signature_algorithms, 10.0.0.80, 68.233.76.12, [[hash=TLS::HashAlgorithm_sha512, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha512, signature=TLS::SignatureAlgorithm_dsa], [hash=TLS::HashAlgorithm_sha512, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha384, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha384, signature=TLS::SignatureAlgorithm_dsa], [hash=TLS::HashAlgorithm_sha384, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha256, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha256, signature=TLS::SignatureAlgorithm_dsa], [hash=TLS::HashAlgorithm_sha256, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha224, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha224, signature=TLS::SignatureAlgorithm_dsa], [hash=TLS::HashAlgorithm_sha224, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_sha1, signature=TLS::SignatureAlgorithm_rsa], [hash=TLS::HashAlgorithm_sha1, signature=TLS::SignatureAlgorithm_dsa], [hash=TLS::HashAlgorithm_sha1, signature=TLS::SignatureAlgorithm_ecdsa], [hash=TLS::HashAlgorithm_md5, signature=TLS::SignatureAlgorithm_rsa]] +tls_extension_heartbeat, 10.0.0.80, 68.233.76.12, TLS::HeartbeatMode_peer_allowed_to_send +==Server hello +10.0.0.80, 68.233.76.12 +Version, TLS::TLSVersion_TLSv12 +ts, 1388442863 +server random, \xa7\x02\xf4'&\x05]|c\x83KN\xb0\x0e6F\xbez\xbb\x0ey\xbf\x0f\x85p\x83\x8dX +session id, \xfa(=\xb63N\xf9\x97;\xf8\x9a\xaa\x91] +[i=4, s=] +[zeek] [SPICY_SSH/3/orig] -> event ssh::banner((1, b"OpenSSH_3.8.1p1")) +[zeek] [SPICY_SSH/3/orig] -> event ssh::banner((2, b"OpenSSH_3.8.1p1")) +[zeek] [SPICY_SSH/3/orig] -> event ssh::banner((3, )) +[zeek] [SPICY_SSH/3/orig] -> event ssh::banner((4, Null)) diff --git a/tests/Baseline/zeek.evt.tuple-enum/output b/tests/Baseline/zeek.evt.tuple-enum/output new file mode 100644 index 000000000..7489474c4 --- /dev/null +++ b/tests/Baseline/zeek.evt.tuple-enum/output @@ -0,0 +1,4 @@ +[$a=TestEnum::A, $b=83] +[$a=TestEnum::A, $b=83] +[i=TupleEnum::TestEnum_A, j=83] +[i=TupleEnum::TestEnum_A, j=83] diff --git a/tests/Baseline/zeek.evt.tuple-optional/output b/tests/Baseline/zeek.evt.tuple-optional/output new file mode 100644 index 000000000..568248dce --- /dev/null +++ b/tests/Baseline/zeek.evt.tuple-optional/output @@ -0,0 +1,2 @@ +[i=83, j=83, k=, s=H-, ss=] +[i=83, j=83, k=, s=H-, ss=] diff --git a/tests/Baseline/zeek.evt.type-converter/output b/tests/Baseline/zeek.evt.type-converter/output new file mode 100644 index 000000000..05496942c --- /dev/null +++ b/tests/Baseline/zeek.evt.type-converter/output @@ -0,0 +1,18 @@ +[$a=b"SSH-2", $b=11824, $c=11599, $d=3.14, $e=1.2.3.4, $f=2001:db8::1428:57ab, $g=True, $h="MyString", $s={1, 2, 3}, $v=[b"A", b"B", b"C"], $l=[b"A", b"B", b"C"]] +[orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp] +T +SSH-2 +11824 +11599 +3.14 +1.2.3.4 +2001:db8::1428:57ab +T +MyString +{ +2, +1, +3 +} +[A, B, C] +[A, B, C] diff --git a/tests/Baseline/zeek.evt.udp/output b/tests/Baseline/zeek.evt.udp/output new file mode 100644 index 000000000..ed94dc95e --- /dev/null +++ b/tests/Baseline/zeek.evt.udp/output @@ -0,0 +1,4 @@ +UDP packet, [orig_h=127.0.0.1, orig_p=12345/udp, resp_h=127.0.0.1, resp_p=31337/udp], T, 12345\x0a +UDP packet, [orig_h=127.0.0.1, orig_p=12345/udp, resp_h=127.0.0.1, resp_p=31337/udp], F, ABCDE\x0a +UDP packet, [orig_h=127.0.0.1, orig_p=12345/udp, resp_h=127.0.0.1, resp_p=31337/udp], T, 67890\x0a +UDP packet, [orig_h=127.0.0.1, orig_p=12345/udp, resp_h=127.0.0.1, resp_p=31337/udp], F, FGHIJ\x0a diff --git a/tests/Baseline/zeek.lib.protocols.dns/conn.log b/tests/Baseline/zeek.lib.protocols.dns/conn.log new file mode 100644 index 000000000..842965eea --- /dev/null +++ b/tests/Baseline/zeek.lib.protocols.dns/conn.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path conn +#open 2020-03-29-15-32-20 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] +964953086.310131 CHhAvVGS1DHFjwGM9 10.20.1.31 53 207.158.192.40 53 udp dns - - - S0 - - 0 D^ 1 461 0 0 - +#close 2020-03-29-15-32-20 diff --git a/tests/Baseline/zeek.lib.protocols.dns/dns.log b/tests/Baseline/zeek.lib.protocols.dns/dns.log new file mode 100644 index 000000000..a993b0a4a --- /dev/null +++ b/tests/Baseline/zeek.lib.protocols.dns/dns.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path dns +#open 2020-03-29-15-32-20 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto trans_id rtt query qclass qclass_name qtype qtype_name rcode rcode_name AA TC RD RA Z answers TTLs rejected +#types time string addr port addr port enum count interval string count string count string count string bool bool bool bool count vector[string] vector[interval] bool +964953086.310131 CHhAvVGS1DHFjwGM9 10.20.1.31 53 207.158.192.40 53 udp 25701 - us.v27.distributed.net - - - - 0 NOERROR T F F T 0 206.109.64.186,216.1.205.81,205.149.163.211,134.53.131.135,134.53.131.192,128.104.18.148,204.152.186.139,63.77.33.226 900.000000,900.000000,900.000000,900.000000,900.000000,900.000000,900.000000,900.000000 F +#close 2020-03-29-15-32-20 diff --git a/tests/Baseline/zeek.lib.protocols.http/conn.log b/tests/Baseline/zeek.lib.protocols.http/conn.log new file mode 100644 index 000000000..2c47384c8 --- /dev/null +++ b/tests/Baseline/zeek.lib.protocols.http/conn.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path conn +#open 2020-03-27-15-16-40 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration orig_bytes resp_bytes conn_state local_orig local_resp missed_bytes history orig_pkts orig_ip_bytes resp_pkts resp_ip_bytes tunnel_parents +#types time string addr port addr port enum string interval count count string bool bool count string count count count count set[string] +1363709111.062053 CHhAvVGS1DHFjwGM9 141.142.228.5 53595 54.243.55.129 80 tcp http 0.068875 160 519 SF - - 0 ShADadFf 8 588 6 839 - +#close 2020-03-27-15-16-40 diff --git a/tests/Baseline/zeek.lib.protocols.http/files.log b/tests/Baseline/zeek.lib.protocols.http/files.log new file mode 100644 index 000000000..9e15dd757 --- /dev/null +++ b/tests/Baseline/zeek.lib.protocols.http/files.log @@ -0,0 +1,11 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path files +#open 2020-03-29-15-26-42 +#fields ts fuid tx_hosts rx_hosts conn_uids source depth analyzers mime_type filename duration local_orig is_orig seen_bytes total_bytes missing_bytes overflow_bytes timedout parent_fuid md5 sha1 sha256 extracted extracted_cutoff extracted_size +#types time string set[addr] set[addr] set[string] string count set[string] string string interval bool bool count count count count bool string string string string string bool count +1363709111.086948 FYWSop3C6glckoyKMi 141.142.228.5 54.243.55.129 CHhAvVGS1DHFjwGM9 HTTP 0 MD5,SHA1 text/plain - 0.000000 - T 11 11 0 0 F - 5eb63bbbe01eeed093cb22bb8f5acdc3 2aae6c35c94fcfb415dbe95f408b9ce91ee846ed - - - - +1363709111.129368 F25UR31MqF16EHYCGk 54.243.55.129 141.142.228.5 CHhAvVGS1DHFjwGM9 HTTP 0 MD5,SHA1 text/json - 0.000000 - F 366 366 0 0 F - c9337794df612aeaa901dcf9fa446bca 6a1582672c203210c6d18d700322060b676365e7 - - - - +#close 2020-03-29-15-26-42 diff --git a/tests/Baseline/zeek.lib.protocols.http/http.log b/tests/Baseline/zeek.lib.protocols.http/http.log new file mode 100644 index 000000000..9372d8cda --- /dev/null +++ b/tests/Baseline/zeek.lib.protocols.http/http.log @@ -0,0 +1,10 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path http +#open 2020-03-29-15-26-42 +#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p trans_depth method host uri referrer version user_agent origin request_body_len response_body_len status_code status_msg info_code info_msg tags username password proxied orig_fuids orig_filenames orig_mime_types resp_fuids resp_filenames resp_mime_types +#types time string addr port addr port count string string string string string string string count count count string count string set[enum] string string set[string] vector[string] vector[string] vector[string] vector[string] vector[string] vector[string] +1363709111.086948 CHhAvVGS1DHFjwGM9 141.142.228.5 53595 54.243.55.129 80 1 POST httpbin.org /post - 1.1 curl/7.29.0 - 11 366 200 OK - - (empty) - - - FYWSop3C6glckoyKMi - text/plain F25UR31MqF16EHYCGk - text/json +#close 2020-03-29-15-26-42 diff --git a/tests/Baseline/zeek.ssh-banner/output b/tests/Baseline/zeek.ssh-banner/output new file mode 100644 index 000000000..738a63d57 --- /dev/null +++ b/tests/Baseline/zeek.ssh-banner/output @@ -0,0 +1,4 @@ +Analyzer::ANALYZER_SPICY_SSH, 4 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], F, 1.99, OpenSSH_3.9p1 +SSH banner, [orig_h=192.150.186.169, orig_p=49244/tcp, resp_h=131.159.14.23, resp_p=22/tcp], T, 2.0, OpenSSH_3.8.1p1 +Analyzer::ANALYZER_SSH, 3 diff --git a/tests/Scripts/.empty b/tests/Scripts/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/tests/Scripts/3rdparty/checkbashisms.pl b/tests/Scripts/3rdparty/checkbashisms.pl new file mode 100755 index 000000000..8dc35e17a --- /dev/null +++ b/tests/Scripts/3rdparty/checkbashisms.pl @@ -0,0 +1,814 @@ +#!/usr/bin/perl + +# This script is essentially copied from /usr/share/lintian/checks/scripts, +# which is: +# Copyright (C) 1998 Richard Braakman +# Copyright (C) 2002 Josip Rodin +# This version is +# Copyright (C) 2003 Julian Gilbey +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +use strict; +use warnings; +use Getopt::Long qw(:config bundling permute no_getopt_compat); +use File::Temp qw/tempfile/; + +sub init_hashes; + +(my $progname = $0) =~ s|.*/||; + +my $usage = <<"EOF"; +Usage: $progname [-n] [-f] [-x] script ... + or: $progname --help + or: $progname --version +This script performs basic checks for the presence of bashisms +in /bin/sh scripts and the lack of bashisms in /bin/bash ones. +EOF + +my $version = <<"EOF"; +This is $progname, from the Debian devscripts package, version ###VERSION### +This code is copyright 2003 by Julian Gilbey , +based on original code which is copyright 1998 by Richard Braakman +and copyright 2002 by Josip Rodin. +This program comes with ABSOLUTELY NO WARRANTY. +You are free to redistribute this code under the terms of the +GNU General Public License, version 2, or (at your option) any later version. +EOF + +my ($opt_echo, $opt_force, $opt_extra, $opt_posix); +my ($opt_help, $opt_version); +my @filenames; + +# Detect if STDIN is a pipe +if (scalar(@ARGV) == 0 && (-p STDIN or -f STDIN)) { + push(@ARGV, '-'); +} + +## +## handle command-line options +## +$opt_help = 1 if int(@ARGV) == 0; + +GetOptions( + "help|h" => \$opt_help, + "version|v" => \$opt_version, + "newline|n" => \$opt_echo, + "force|f" => \$opt_force, + "extra|x" => \$opt_extra, + "posix|p" => \$opt_posix, + ) + or die +"Usage: $progname [options] filelist\nRun $progname --help for more details\n"; + +if ($opt_help) { print $usage; exit 0; } +if ($opt_version) { print $version; exit 0; } + +$opt_echo = 1 if $opt_posix; + +my $mode = 0; +my $issues = 0; +my $status = 0; +my $makefile = 0; +my (%bashisms, %string_bashisms, %singlequote_bashisms); + +my $LEADIN + = qr'(?:(?:^|[`&;(|{])\s*|(?:(?:if|elif|while)(?:\s+!)?|then|do|shell)\s+)'; +init_hashes; + +my @bashisms_keys = sort keys %bashisms; +my @string_bashisms_keys = sort keys %string_bashisms; +my @singlequote_bashisms_keys = sort keys %singlequote_bashisms; + +foreach my $filename (@ARGV) { + my $check_lines_count = -1; + + my $display_filename = $filename; + + if ($filename eq '-') { + my $tmp_fh; + ($tmp_fh, $filename) + = tempfile("chkbashisms_tmp.XXXX", TMPDIR => 1, UNLINK => 1); + while (my $line = ) { + print $tmp_fh $line; + } + close($tmp_fh); + $display_filename = "(stdin)"; + } + + if (!$opt_force) { + $check_lines_count = script_is_evil_and_wrong($filename); + } + + if ($check_lines_count == 0 or $check_lines_count == 1) { + warn +"script $display_filename does not appear to be a /bin/sh script; skipping\n"; + next; + } + + if ($check_lines_count != -1) { + warn +"script $display_filename appears to be a shell wrapper; only checking the first " + . "$check_lines_count lines\n"; + } + + unless (open C, '<', $filename) { + warn "cannot open script $display_filename for reading: $!\n"; + $status |= 2; + next; + } + + $issues = 0; + $mode = 0; + my $cat_string = ""; + my $cat_indented = 0; + my $quote_string = ""; + my $last_continued = 0; + my $continued = 0; + my $found_rules = 0; + my $buffered_orig_line = ""; + my $buffered_line = ""; + my %start_lines; + + while () { + next unless ($check_lines_count == -1 or $. <= $check_lines_count); + + if ($. == 1) { # This should be an interpreter line + if (m,^\#!\s*(?:\S+/env\s+)?(\S+),) { + my $interpreter = $1; + + if ($interpreter =~ m,(?:^|/)make$,) { + init_hashes if !$makefile++; + $makefile = 1; + } else { + init_hashes if $makefile--; + $makefile = 0; + } + next if $opt_force; + + if ($interpreter =~ m,(?:^|/)bash$,) { + $mode = 1; + } elsif ($interpreter !~ m,(?:^|/)(sh|dash|posh)$,) { +### ksh/zsh? + warn +"script $display_filename does not appear to be a /bin/sh script; skipping\n"; + $status |= 2; + last; + } + } else { + warn +"script $display_filename does not appear to have a \#! interpreter line;\nyou may get strange results\n"; + } + } + + chomp; + my $orig_line = $_; + + # We want to remove end-of-line comments, so need to skip + # comments that appear inside balanced pairs + # of single or double quotes + + # Remove comments in the "quoted" part of a line that starts + # in a quoted block? The problem is that we have no idea + # whether the program interpreting the block treats the + # quote character as part of the comment or as a quote + # terminator. We err on the side of caution and assume it + # will be treated as part of the comment. + # s/^(?:.*?[^\\])?$quote_string(.*)$/$1/ if $quote_string ne ""; + + # skip comment lines + if ( m,^\s*\#, + && $quote_string eq '' + && $buffered_line eq '' + && $cat_string eq '') { + next; + } + + # Remove quoted strings so we can more easily ignore comments + # inside them + s/(^|[^\\](?:\\\\)*)\'(?:\\.|[^\\\'])+\'/$1''/g; + s/(^|[^\\](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g; + + # If inside a quoted string, remove everything before the quote + s/^.+?\'// + if ($quote_string eq "'"); + s/^.+?[^\\]\"// + if ($quote_string eq '"'); + + # If the remaining string contains what looks like a comment, + # eat it. In either case, swap the unmodified script line + # back in for processing. + if (m/(?:^|[^[\\])[\s\&;\(\)](\#.*$)/) { + $_ = $orig_line; + s/\Q$1\E//; # eat comments + } else { + $_ = $orig_line; + } + + # Handle line continuation + if (!$makefile && $cat_string eq '' && m/\\$/) { + chop; + $buffered_line .= $_; + $buffered_orig_line .= $orig_line . "\n"; + next; + } + + if ($buffered_line ne '') { + $_ = $buffered_line . $_; + $orig_line = $buffered_orig_line . $orig_line; + $buffered_line = ''; + $buffered_orig_line = ''; + } + + if ($makefile) { + $last_continued = $continued; + if (/[^\\]\\$/) { + $continued = 1; + } else { + $continued = 0; + } + + # Don't match lines that look like a rule if we're in a + # continuation line before the start of the rules + if (/^[\w%-]+:+\s.*?;?(.*)$/ + and !($last_continued and !$found_rules)) { + $found_rules = 1; + $_ = $1 if $1; + } + + last + if m%^\s*(override\s|export\s)?\s*SHELL\s*:?=\s*(/bin/)?bash\s*%; + + # Remove "simple" target names + s/^[\w%.-]+(?:\s+[\w%.-]+)*::?//; + s/^\t//; + s/(?|<|;|\Z)/o + and m/$LEADIN(\.\s+[^\s;\`:]+\s+([^\s;]+))/o) { + if ($2 =~ /^(\&|\||\d?>|<)/) { + # everything is ok + ; + } else { + $found = 1; + $match = $1; + $explanation = "sourced script with arguments"; + output_explanation($display_filename, $orig_line, + $explanation); + } + } + + # Remove "quoted quotes". They're likely to be inside + # another pair of quotes; we're not interested in + # them for their own sake and removing them makes finding + # the limits of the outer pair far easier. + $line =~ s/(^|[^\\\'\"])\"\'\"/$1/g; + $line =~ s/(^|[^\\\'\"])\'\"\'/$1/g; + + foreach my $re (@singlequote_bashisms_keys) { + my $expl = $singlequote_bashisms{$re}; + if ($line =~ m/($re)/) { + $found = 1; + $match = $1; + $explanation = $expl; + output_explanation($display_filename, $orig_line, + $explanation); + } + } + + my $re = '(?); + } + } + + # $cat_line contains the version of the line we'll check + # for heredoc delimiters later. Initially, remove any + # spaces between << and the delimiter to make the following + # updates to $cat_line easier. However, don't remove the + # spaces if the delimiter starts with a -, as that changes + # how the delimiter is searched. + my $cat_line = $line; + $cat_line =~ s/(<\<-?)\s+(?!-)/$1/g; + + # Ignore anything inside single quotes; it could be an + # argument to grep or the like. + $line =~ s/(^|[^\\\"](?:\\\\)*)\'(?:\\.|[^\\\'])+\'/$1''/g; + + # As above, with the exception that we don't remove the string + # if the quote is immediately preceded by a < or a -, so we + # can match "foo <<-?'xyz'" as a heredoc later + # The check is a little more greedy than we'd like, but the + # heredoc test itself will weed out any false positives + $cat_line =~ s/(^|[^<\\\"-](?:\\\\)*)\'(?:\\.|[^\\\'])+\'/$1''/g; + + $re = '(?); + } + } + + foreach my $re (@string_bashisms_keys) { + my $expl = $string_bashisms{$re}; + if ($line =~ m/($re)/) { + $found = 1; + $match = $1; + $explanation = $expl; + output_explanation($display_filename, $orig_line, + $explanation); + } + } + + # We've checked for all the things we still want to notice in + # double-quoted strings, so now remove those strings as well. + $line =~ s/(^|[^\\\'](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g; + $cat_line =~ s/(^|[^<\\\'-](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g; + foreach my $re (@bashisms_keys) { + my $expl = $bashisms{$re}; + if ($line =~ m/($re)/) { + $found = 1; + $match = $1; + $explanation = $expl; + output_explanation($display_filename, $orig_line, + $explanation); + } + } + # This check requires the value to be compared, which could + # be done in the regex itself but requires "use re 'eval'". + # So it's better done in its own + if ($line =~ m/$LEADIN((?:exit|return)\s+(\d{3,}))/o && $2 > 255) { + $explanation = 'exit|return status code greater than 255'; + output_explanation($display_filename, $orig_line, + $explanation); + } + + # Only look for the beginning of a heredoc here, after we've + # stripped out quoted material, to avoid false positives. + if ($cat_line + =~ m/(?:^|[^<])\<\<(\-?)\s*(?:(?!<|'|")((?:[^\s;>|]+(?:(?<=\\)[\s;>|])?)+)|[\'\"](.*?)[\'\"])/ + ) { + $cat_indented = ($1 && $1 eq '-') ? 1 : 0; + my $quoted = defined($3); + $cat_string = $quoted ? $3 : $2; + unless ($quoted) { + # Now strip backslashes. Keep the position of the + # last match in a variable, as s/// resets it back + # to undef, but we don't want that. + my $pos = 0; + pos($cat_string) = $pos; + while ($cat_string =~ s/\G(.*?)\\/$1/) { + # position += length of match + the character + # that followed the backslash: + $pos += length($1) + 1; + pos($cat_string) = $pos; + } + } + $start_lines{'cat_string'} = $.; + } + } + } + + warn +"error: $display_filename: Unterminated heredoc found, EOF reached. Wanted: <$cat_string>, opened in line $start_lines{'cat_string'}\n" + if ($cat_string ne ''); + warn +"error: $display_filename: Unterminated quoted string found, EOF reached. Wanted: <$quote_string>, opened in line $start_lines{'quote_string'}\n" + if ($quote_string ne ''); + warn "error: $display_filename: EOF reached while on line continuation.\n" + if ($buffered_line ne ''); + + close C; + + if ($mode && !$issues) { + warn "could not find any possible bashisms in bash script $filename\n"; + $status |= 4; + } +} + +exit $status; + +sub output_explanation { + my ($filename, $line, $explanation) = @_; + + if ($mode) { + # When examining a bash script, just flag that there are indeed + # bashisms present + $issues = 1; + } else { + warn "possible bashism in $filename line $. ($explanation):\n$line\n"; + $status |= 1; + } +} + +# Returns non-zero if the given file is not actually a shell script, +# just looks like one. +sub script_is_evil_and_wrong { + my ($filename) = @_; + my $ret = -1; + # lintian's version of this function aborts if the file + # can't be opened, but we simply return as the next + # test in the calling code handles reporting the error + # itself + open(IN, '<', $filename) or return $ret; + my $i = 0; + my $var = "0"; + my $backgrounded = 0; + local $_; + while () { + chomp; + next if /^#/o; + next if /^$/o; + last if (++$i > 55); + if ( + m~ + # the exec should either be "eval"ed or a new statement + (^\s*|\beval\s*[\'\"]|(;|&&|\b(then|else))\s*) + + # eat anything between the exec and $0 + exec\s*.+\s* + + # optionally quoted executable name (via $0) + .?\$$var.?\s* + + # optional "end of options" indicator + (--\s*)? + + # Match expressions of the form '${1+$@}', '${1:+"$@"', + # '"${1+$@', "$@", etc where the quotes (before the dollar + # sign(s)) are optional and the second (or only if the $1 + # clause is omitted) parameter may be $@ or $*. + # + # Finally the whole subexpression may be omitted for scripts + # which do not pass on their parameters (i.e. after re-execing + # they take their parameters (and potentially data) from stdin + .?(\$\{1:?\+.?)?(\$(\@|\*))?~x + ) { + $ret = $. - 1; + last; + } elsif (/^\s*(\w+)=\$0;/) { + $var = $1; + } elsif ( + m~ + # Match scripts which use "foo $0 $@ &\nexec true\n" + # Program name + \S+\s+ + + # As above + .?\$$var.?\s* + (--\s*)? + .?(\$\{1:?\+.?)?(\$(\@|\*))?.?\s*\&~x + ) { + + $backgrounded = 1; + } elsif ( + $backgrounded + and m~ + # the exec should either be "eval"ed or a new statement + (^\s*|\beval\s*[\'\"]|(;|&&|\b(then|else))\s*) + exec\s+true(\s|\Z)~x + ) { + + $ret = $. - 1; + last; + } elsif (m~\@DPATCH\@~) { + $ret = $. - 1; + last; + } + + } + close IN; + return $ret; +} + +sub init_hashes { + + %bashisms = ( + qr'(?:^|\s+)function [^<>\(\)\[\]\{\};|\s]+(\s|\(|\Z)' => + q<'function' is useless>, + $LEADIN . qr'select\s+\w+' => q<'select' is not POSIX>, + qr'(test|-o|-a)\s*[^\s]+\s+==\s' => q, + qr'\[\s+[^\]]+\s+==\s' => q, + qr'\s\|\&' => q, + qr'[^\\\$]\{([^\s\\\}]*?,)+[^\\\}\s]*\}' => q, + qr'\{\d+\.\.\d+(?:\.\.\d+)?\}' => + q, + qr'(?i)\{[a-z]\.\.[a-z](?:\.\.\d+)?\}' => q, + qr'(?:^|\s+)\w+\[\d+\]=' => q, + $LEADIN + . qr'read\s+(?:-[a-qs-zA-Z\d-]+)' => + q, + $LEADIN + . qr'read\s*(?:-\w+\s*)*(?:\".*?\"|[\'].*?[\'])?\s*(?:;|$)' => + q, + $LEADIN . qr'echo\s+(-n\s+)?-n?en?\s' => q, + $LEADIN . qr'exec\s+-[acl]' => q, + $LEADIN . qr'let\s' => q, + qr'(? q<'((' should be '$(('>, + qr'(?:^|\s+)(\[|test)\s+-a' => q, + qr'\&>' => qword 2\>&1>, + qr'(<\&|>\&)\s*((-|\d+)[^\s;|)}`&\\\\]|[^-\d\s]+(? + qword 2\>&1>, + qr'\[\[(?!:)' => + q, + qr'/dev/(tcp|udp)' => q, + $LEADIN . qr'builtin\s' => q, + $LEADIN . qr'caller\s' => q, + $LEADIN . qr'compgen\s' => q, + $LEADIN . qr'complete\s' => q, + $LEADIN . qr'declare\s' => q, + $LEADIN . qr'dirs(\s|\Z)' => q, + $LEADIN . qr'disown\s' => q, + $LEADIN . qr'enable\s' => q, + $LEADIN . qr'mapfile\s' => q, + $LEADIN . qr'readarray\s' => q, + $LEADIN . qr'shopt(\s|\Z)' => q, + $LEADIN . qr'suspend\s' => q, + $LEADIN . qr'time\s' => q